0Pricing
Java Academy · 课时

组合 Future:allOf 与 anyOf

使用 allOf 等待所有 Future 完成,并使用 anyOf 进行竞速,以实现超时和回退逻辑

组合 Future:allOf 与 anyOf 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。

为什么要组合 Future?

当彼此独立的异步任务可以并行运行时,您通常需要等待全部任务(或第一个任务)完成后再继续。allOf 和 anyOf 分别处理这两种情况。

allOf:等待所有 Future

CompletableFuture.allOf(futures...) 会返回一个 CompletableFuture<Void>,当所有输入 future 都完成时,它也会完成。如果任意一个 future 失败,allOf 也会失败。

CompletableFuture<String> f1 = fetchUserAsync();
CompletableFuture<List<Order>> f2 = fetchOrdersAsync();
CompletableFuture<Double> f3 = fetchBalanceAsync();
CompletableFuture<Void> all = CompletableFuture.allOf(f1, f2, f3);
all.join(); // wait for all three
String user   = f1.join();
List<Order> orders = f2.join();
Double balance = f3.join();

收集 allOf 的结果

由于 allOf 返回 Void,因此请在 allOf.join() 之后,对每个单独的 future 调用 join() 来收集结果。

List<CompletableFuture<String>> futures = List.of(
    fetchAsync("url1"), fetchAsync("url2"), fetchAsync("url3"));
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
    .thenApply(v -> futures.stream()
        .map(CompletableFuture::join)
        .collect(Collectors.toList()))
    .thenAccept(results -> results.forEach(System.out::println))
    .join();

anyOf:第一个完成者获胜

CompletableFuture.anyOf(futures...) 会返回一个 CompletableFuture<Object>,其结果来自第一个完成的 future,与类型无关。

CompletableFuture<Object> first = CompletableFuture.anyOf(
    fetchFromCacheAsync(),
    fetchFromDbAsync(),
    fetchFromApiAsync()
);
System.out.println("Fastest result: " + first.join());

使用 anyOf 实现超时备用方案

让实际计算与超时 future 进行竞速。哪个先完成就采用哪个结果,从而实现非阻塞的超时模式。

CompletableFuture<String> real    = fetchDataAsync();
CompletableFuture<String> timeout = CompletableFuture
    .supplyAsync(() -> {
        try { Thread.sleep(2000); } catch (InterruptedException e) {}
        return "timeout-default";
    });
String result = (String) CompletableFuture.anyOf(real, timeout).join();

orTimeout(Java 9+)

如果 future 未能及时完成,orTimeout(time, unit) 会使用 TimeoutException 完成它。与手动使用 anyOf 实现超时的模式相比,这种方式更加简洁。

CompletableFuture<String> result = fetchDataAsync()
    .orTimeout(3, TimeUnit.SECONDS)
    .exceptionally(ex -> "timeout-default");
System.out.println(result.join());

使用 allOf 并行发起 HTTP 调用

并行调用多个服务,然后合并结果。当服务彼此独立时,这比顺序调用快得多。

List<String> urls = List.of(url1, url2, url3);
List<CompletableFuture<String>> futures = urls.stream()
    .map(url -> CompletableFuture.supplyAsync(() -> httpGet(url)))
    .collect(Collectors.toList());
List<String> responses = CompletableFuture
    .allOf(futures.toArray(new CompletableFuture[0]))
    .thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()))
    .join();

allOf 中的错误处理

如果 allOf 中的一个 future 失败,返回的 future 会立即失败。请使用 exceptionally 包装各个 future,以避免一个失败导致整个批次失败。

List<CompletableFuture<String>> safe = futures.stream()
    .map(f -> f.exceptionally(ex -> "ERROR: " + ex.getMessage()))
    .collect(Collectors.toList());
CompletableFuture.allOf(safe.toArray(new CompletableFuture[0])).join();

使用 allOf 处理不同类型

allOf 接受不同类型的 future。完成并合并结果后,请将每个 future 的结果转换为预期类型。

CompletableFuture<User> userFuture   = fetchUser(id);
CompletableFuture<Config> configFuture = fetchConfig();
CompletableFuture.allOf(userFuture, configFuture).join();
User   user   = userFuture.join();
Config config = configFuture.join();

组合 allOf 和 thenApply

请在 allOf 上串联 thenApply,以便在所有 future 完成后立即聚合结果。

CompletableFuture<String> summary =
    CompletableFuture.allOf(f1, f2, f3)
        .thenApply(v -> "User: " + f1.join() +
                       ", Orders: " + f2.join().size() +
                       ", Balance: " + f3.join());
System.out.println(summary.join());

线程池注意事项

allOf / anyOf 调用中的所有 future 都可以并发运行。请确保线程池拥有足够的线程,或使用虚拟线程(Java 21+),以避免线程饥饿。

// With virtual threads (Java 21+):
ExecutorService vt = Executors.newVirtualThreadPerTaskExecutor();
CompletableFuture<String> f = CompletableFuture.supplyAsync(() -> httpGet(url), vt);

取消传播

当一个 future 完成或被取消时,allOf / anyOf 不会自动取消其余 future。如有需要,请分别取消它们。

CompletableFuture<Object> first = CompletableFuture.anyOf(f1, f2, f3);
first.thenRun(() -> Stream.of(f1, f2, f3).forEach(f -> f.cancel(true)));

快速检查

CompletableFuture.anyOf 会返回什么?

回顾

allOf 等待所有 future(汇聚)。anyOf 获取第一个结果(竞速)。请使用 orTimeout 处理超时。使用 exceptionally 包装各个 future,以避免一个失败导致整个批次失败。

常见问题解答

「组合 Future:allOf 与 anyOf」课时是免费的吗?

是的 — 「组合 Future:allOf 与 anyOf」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。

「组合 Future:allOf 与 anyOf」这节课中我会学到什么?

使用 allOf 等待所有 Future 完成,并使用 anyOf 进行竞速,以实现超时和回退逻辑 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Java Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「组合 Future:allOf 与 anyOf」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Java Academy 课中编写并运行代码吗?

能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 创建和完成 CompletableFutures
  2. 使用 thenApply 和 thenCompose 链式处理
  3. 组合 Future:allOf 与 anyOf
  4. 错误处理与异步 HTTP 流水线
← 返回 Java Academy