组合 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 反馈 — 无需本地设置。
此课程中的所有课时
- 创建和完成 CompletableFutures
- 使用 thenApply 和 thenCompose 链式处理
- 组合 Future:allOf 与 anyOf
- 错误处理与异步 HTTP 流水线