Java CompletableFuture
CompletableFuture 如何组合异步任务、传递异常与配置执行器:避免阻塞式回调地狱。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java CompletableFuture
这个概念为什么出现
Future.get() 会阻塞;多依赖组装易成回调泥潭。CompletableFuture 提供声明式异步组合:thenApply/thenCompose/exceptionally 等,把任务图表达清楚。
[!abstract] 一句话理解 CompletableFuture 是可手动完成、可组合的异步结果;组合链要显式指定 Executor,并统一处理异常与超时。
最小可运行示例
场景:并行查库存与价格,再汇总下单视图
import java.util.concurrent.*;
public class OrderViewAsync {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(2);
try {
CompletableFuture<Integer> stock = CompletableFuture.supplyAsync(() -> 3, pool);
CompletableFuture<Integer> price = CompletableFuture.supplyAsync(() -> 100, pool);
CompletableFuture<String> view = stock.thenCombineAsync(price,
(s, p) -> "stock=" + s + ",price=" + p, pool);
System.out.println(view.get(1, TimeUnit.SECONDS));
} finally {
pool.shutdown();
}
}
}
建议运行:
javac OrderViewAsync.java && java OrderViewAsync
期望输出:
stock=3,price=100
结合场景再看三个关注点
- supplyAsync 指定 pool,别默认打满 commonPool。
- thenCombine 表达汇合。
- get 设超时,防永久阻塞。
核心概念与准确模型
1. 创建
supplyAsync/runAsync/completedFuture
2. 组合
thenApply变换thenCompose扁平嵌套异步thenCombine两路汇合allOf/anyOf
3. 异常
exceptionally/handle/whenComplete- 未处理异常在 get 时以 ExecutionException 出现
4. 超时
orTimeout/completeOnTimeout(版本相关)
边界情况与反直觉行为
- 链上线程落在哪取决于 Async 后缀与 Executor。
- 阻塞式调用塞进 commonPool 会饿死。
- 取消语义有限,需协作。
常见误区
[!warning] 常见误区:在 thenApply 里做重阻塞 IO 还不换池 IO 用专用 Executor 或虚拟线程策略。
工程实践
- 每条业务异步边界明确线程池。
- 统一包装异常为领域错误。
- 指标记录耗时与超时。
- 与响应式库边界分清,不混风格。
本节总结
- CF=异步组合工具
- Executor + 超时 + 异常
- 汇合与扁平化是核心技能
自测题
- thenApply 与 thenCompose 差在哪?
- 为什么要避免默认 commonPool 跑阻塞任务?
参考答案
- apply 映射值;compose 用于返回另一个 CompletionStage 并扁平化。
- commonPool 被阻塞会拖垮其它并行任务。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| CompletableFuture API | API | 组合方法 |