CompletableFuture组合异步任务需区分任务关系:不依赖时用allOf+join手动取结果并指定线程池;两任务合并用thenCombine;竞速场景用applyToEither/acceptEither;务必自定义线程池并用handle/exceptionally处理异常。

用 CompletableFuture 组合多个独立的异步任务,核心是区分任务关系:不依赖、并行执行、统一收口。关键不是“一起发”,而是“发完怎么等、怎么拿结果”。
并行发起 + 等全部完成(allOf)
适用于多个任务互不依赖,但业务逻辑需等它们全跑完再处理汇总结果。
-
allOf 返回的是 CompletableFuture
,它只表示“都完成了”,不带结果;不能直接 get() 拿数据 - 必须配合原始 future 列表,手动 collect 结果;推荐用 join()(不抛检异常)而非 get()
- 记得显式传入自定义线程池,避免挤占 ForkJoinPool.commonPool()
示例:
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> fetchUser(), pool); CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> fetchOrder(), pool); CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> fetchAddress(), pool); // 启动全部,但不阻塞 CompletableFuture.allOf(f1, f2, f3).join(); // 等全部结束 // 安全取结果(每个 future 调用 join,自动处理异常) List<String> results = Arrays.asList(f1.join(), f2.join(), f3.join());
并行发起 + 合并两个结果(thenCombine)
当只有两个任务需要结果相加、拼接或计算时,比 allOf + 手动取值更简洁、类型安全。
立即学习“Java免费学习笔记(深入)”;
- thenCombine 是链式调用,返回新的 CompletableFuture,类型由 lambda 决定
- 两个任务真正并行执行,不是先后触发;失败任一,整个链失败(除非加 exceptionally)
- 适合组合数据库查价 + 远程算税、查用户 + 查权限等两两配对场景
示例:
CompletableFuture<BigDecimal> price = CompletableFuture.supplyAsync(() -> db.getPrice("A"), pool);
CompletableFuture<BigDecimal> discount = CompletableFuture.supplyAsync(() -> api.getDiscount("A"), pool);
CompletableFuture<BigDecimal> finalPrice = price.thenCombine(discount, (p, d) -> p.subtract(d));
return finalPrice.join();
并行发起 + 取最先完成的结果(applyToEither / acceptEither)
适用于“竞速”场景,比如主备服务、多 CDN 源、降级兜底——谁快用谁,不等慢的。
- applyToEither 返回新 CompletableFuture(可转换结果),acceptEither 仅消费不产出
- 只要有一个完成就触发,另一个会继续运行但结果被忽略(不会取消)
- 注意:若两个都异常,会抛出 CompletionException
示例:
CompletableFuture<String> fast = CompletableFuture.supplyAsync(() -> callPrimary(), pool); CompletableFuture<String> fallback = CompletableFuture.supplyAsync(() -> callBackup(), pool); String result = fast.applyToEither(fallback, s -> "OK: " + s).join();
组合时务必注意线程池和异常处理
生产环境不能靠默认线程池,也不能让异常静默吞掉。
- 所有 supplyAsync/runAsync 都应传入专用线程池(如 ioPool、computePool),避免混用
- 用 handle() 替代 thenAccept + 单独 try-catch:它能同时拿到结果和异常,统一处理
- 对可能失败的任务,用 exceptionally() 提供降级值,或用 orTimeout() 加超时控制
例如:
CompletableFuture<Data> task = CompletableFuture.supplyAsync(() -> riskyCall(), ioPool)
.orTimeout(3, TimeUnit.SECONDS)
.exceptionally(ex -> new Data("fallback"))
.handle((data, ex) -> {
if (ex != null) log.warn("task failed", ex);
return data;
});


















