CompletableFuture 是 Java 8 提供的真正异步编程工具,支持组合、容错与线程池指定;它通过 supplyAsync 启动任务,默认使用 ForkJoinPool,可用 thenCompose 扁平化链式异步调用,需用 exceptionally 或 handle 捕获异常,避免 join/get 阻塞主线程。

CompletableFuture 不是“另一个 Future”,它是 Java 8 引入的、真正能写异步逻辑的工具——能组合、能容错、能指定线程池,而 Future 只能阻塞等待。
CompletableFuture 怎么替代 new Thread() 或 ExecutorService.submit() 写异步任务
直接用 CompletableFuture.supplyAsync() 启动异步计算,它默认走 ForkJoinPool.commonPool(),比手建线程或反复 submit 更轻量、更可控。
- 需要自定义线程池?传第二个参数:
CompletableFuture.supplyAsync(() -> doWork(), myExecutor) - 别在 lambda 里吞异常:supplyAsync 的 lambda 若抛出未检查异常,会包装成
CompletionException,下游要用exceptionally()或handle()捕获 - 避免在主线程里调用
join()或get()等待结果——这会阻塞,失去异步意义;优先用thenApply()、thenAccept()做链式响应
多个异步任务怎么串起来(比如查 DB → 调 HTTP → 组装返回)
用 thenCompose() 而不是 thenApply():前者用于返回另一个 CompletableFuture 的场景,能扁平化嵌套;后者只适合同步转换。
-
thenApply():输入是 T,输出是 R(同步转换),比如cf.thenApply(s -> s.toUpperCase()) -
thenCompose():输入是 T,输出是CompletableFuture<R>,比如dbQuery().thenCompose(data -> httpCall(data)) - 错误传递不自动跨链:一个环节 fail,后续
thenApply不执行,但thenAccept也不执行——得靠exceptionally()或handle()主动接住
CompletableFuture.allOf() 和 CompletableFuture.anyOf() 容易踩哪些坑
allOf() 返回 CompletableFuture<Void>,不带结果;anyOf() 返回 CompletableFuture<Object>,类型擦除严重——这两个 API 本身就不适合直接取值。
立即学习“Java免费学习笔记(深入)”;
- 要聚合多个异步结果?别用
allOf().join()后再手动 get,改用Collectors.collectingAndThen()配合list.stream().map(CompletableFuture::join).collect(...) -
anyOf()返回的Object需要强转,且无法知道是哪个 future 先完成;真要“取最快结果 + 类型安全”,得自己封装一层,用whenComplete()记录首个完成者 - 任一子 future 抛异常,
allOf()仍会完成(只是状态为 exception),但不会中断其它子任务——它不提供“失败即停”语义
为什么 .join() 和 .get() 表现不同,什么时候该用哪个
join() 是 CompletableFuture 自己的方法,抛 CompletionException;get() 继承自 Future,抛 ExecutionException。实际开发中,只要没特殊兼容需求,统一用 join() 更简洁。
-
join()不受中断影响,无法被Thread.interrupt()打断;get(long, TimeUnit)可以设超时,但join()没有重载版本 - 测试或脚手代码里用
join()没问题;生产服务中,除非你明确要阻塞当前线程(比如 CLI 工具、单元测试 teardown),否则应避免任何join()/get() - 注意:
join()在未完成时会阻塞,但它不会释放当前线程持有的锁(如 synchronized 块内调用),可能引发死锁——这点常被忽略
CompletableFuture 的复杂性不在 API 数量,而在「完成状态传播」和「线程上下文切换」这两点上。多数 bug 来自假设某个回调一定在某线程执行,或者忘了异常根本不会自动向上传递到 thenApply 链里。










