CompletableFuture详解~allOf

当所有的阶段都完成后创建一个阶段

上一个例子是当任意一个阶段完成后接着处理,接下来的两个例子演示当所有的阶段完成后才继续处理, 同步地方式和异步地方式两种。

static void allOfExample() {
    StringBuilder result = new StringBuilder();
    List messages = Arrays.asList("a", "b", "c");
    List<CompletableFuture> futures = messages.stream()
            .map(msg -> CompletableFuture.completedFuture(msg).thenApply(s -> delayedUpperCase(s)))
            .collect(Collectors.toList());
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])).whenComplete((v, th) -> {
        futures.forEach(cf -> assertTrue(isUpperCase(cf.getNow(null))));
        result.append("done");
    });
    assertTrue("Result was empty", result.length() > 0);
}

当所有的阶段都完成后异步地创建一个阶段

使用thenApplyAsync()替换那些单个的CompletableFutures的方法,allOf()会在通用池中的线程中异步地执行。所以我们需要调用join方法等待它完成。

static void allOfAsyncExample() {
    StringBuilder result = new StringBuilder();
    List messages = Arrays.asList("a", "b", "c");
    List<CompletableFuture> futures = messages.stream()
            .map(msg -> CompletableFuture.completedFuture(msg).thenApplyAsync(s -> delayedUpperCase(s)))
            .collect(Collectors.toList());
    CompletableFuture allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))
            .whenComplete((v, th) -> {
                futures.forEach(cf -> assertTrue(isUpperCase(cf.getNow(null))));
                result.append("done");
            });
    allOf.join();
    assertTrue("Result was empty", result.length() > 0);
}

真实的例子

现在你已经了解了CompletionStage 和 CompletableFuture 的一些函数的功能,下面的例子是一个实践场景:

  1. 首先异步调用cars方法获得Car的列表,它返回CompletionStage场景。cars消费一个远程的REST API。

  2. 然后我们复合一个CompletionStage填写每个汽车的评分,通过rating(manufacturerId)返回一个CompletionStage, 它会异步地获取汽车的评分(可能又是一个REST API调用)

  3. 当所有的汽车填好评分后,我们结束这个列表,所以我们调用allOf得到最终的阶段, 它在前面阶段所有阶段完成后才完成。

  4. 在最终的阶段调用whenComplete(),我们打印出每个汽车和它的评分。

cars().thenCompose(cars -> {
    List<CompletionStage> updatedCars = cars.stream()
            .map(car -> rating(car.manufacturerId).thenApply(r -> {
                car.setRating(r);
                return car;
            })).collect(Collectors.toList());
 
    CompletableFuture done = CompletableFuture
            .allOf(updatedCars.toArray(new CompletableFuture[updatedCars.size()]));
    return done.thenApply(v -> updatedCars.stream().map(CompletionStage::toCompletableFuture)
            .map(CompletableFuture::join).collect(Collectors.toList()));
}).whenComplete((cars, th) -> {
    if (th == null) {
        cars.forEach(System.out::println);
    } else {
        throw new RuntimeException(th);
    }
}).toCompletableFuture().join();

因为每个汽车的实例都是独立的,得到每个汽车的评分都可以异步地执行,这会提高系统的性能(延迟),而且,等待所有的汽车评分被处理使用的是allOf方法,而不是手工的线程等待(Thread#join() 或 a CountDownLatch)。

### 使用 `CompletableFuture.allOf().join()` 的作用及用法 `CompletableFuture.allOf()` 方法用于创建一个新的 `CompletableFuture` 实例,该实例会在给定的所有输入 `CompletableFuture` 都完成时才完成。这意味着它会等待所有传入的 `CompletableFuture` 完成,无论是正常结束还是异常终止。 #### `.join()` 方法的作用 `.join()` 是一种非阻塞的方式获取 `CompletableFuture` 的结果。如果调用此方法时 `CompletableFuture` 还未完成,则当前线程会被挂起直到其完成并返回结果。如果有任何 `CompletableFuture` 抛出了未经检查的异常,则这个异常将会被传播到调用者处[^1]。 因此,当使用 `CompletableFuture.allOf().join()` 组合时: - 所有传递给 `allOf` 的 `CompletableFuture` 将并发执行。 - 调用 `join()` 后,主线程将暂停直至所有子任务都已完成。 - 如果任何一个子任务失败,整个组合操作也会标记为失败,并且可以通过捕获异常来处理这些错误情况。 下面是一个简单的例子展示如何利用 `CompletableFuture.allOf.join()` 来实现多个异步任务的同时运行以及收集它们的结果和可能发生的异常: ```java import java.util.Arrays; import java.util.List; import java.util.concurrent.CompletableFuture; public class CompletableFutureExample { public static void main(String[] args) throws Exception { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 创建一系列 CompletableFutures 并让其中一个故意抛出异常 List<CompletableFuture<String>> futures = numbers.stream() .map(n -> n == 2 ? failingFuture() : successfulFuture(n)) .toList(); try { // 等待所有 future 完成 (成功或失败), 不关心具体哪个完成了. CompletableFuture<Void> allOf = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); // 此处会一直等到所有任务完成才会继续往下走 allOf.join(); System.out.println("All tasks have completed."); // 收集各个 task 的最终结果 for (var future : futures) { try { String result = future.get(); // 可能再次触发异常 System.out.println(result); } catch (Exception e) { System.err.printf("Caught exception from one of the futures: %s%n", e.getMessage()); } } } catch (Exception e) { System.err.printf("An error occurred while waiting for completion: %s%n", e.getMessage()); } } private static CompletableFuture<String> successfulFuture(int number){ return CompletableFuture.supplyAsync(() -> "Result of " + number * 2); } private static CompletableFuture<String> failingFuture(){ return CompletableFuture.failedFuture(new RuntimeException("Task failed")); } } ``` 在这个例子中,可以看到即使有一个任务失败了,其他成功的任务仍然可以被正确地处理和打印出来。通过这种方式可以在不影响整体流程的情况下优雅地处理个别任务中的异常状况[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值