completablefuture通过链式调用(thenapply/thenaccept/thenrun等)将异步流程扁平化,支持结果组合(allof/anyof/thencombine)、非阻塞异常处理(exceptionally/handle/whencomplete)及自定义线程池控制,避免commonpool瓶颈。

链式调用:让异步流程像同步代码一样清晰
CompletableFuture 的核心优势之一,是把嵌套回调“拉平”成可读性强的流水线。每个 then 方法都基于前一个任务的结果创建新阶段,且不阻塞主线程。
关键要点:
- thenApply:接收上一阶段结果,返回新值(如字符串拼接、对象转换),适用于有返回值的中间处理
- thenAccept:只消费结果,不返回新值(如日志打印、入库),适合副作用操作
- thenRun:既不接收结果也不返回值,纯后续动作(如清理资源)
- 所有 thenXXX 方法默认在前一任务完成的**同一线程池**中执行;如需切换线程池,可用 thenApplyAsync 等带 Async 后缀的方法显式指定
变量结果组合:并行任务与结果聚合
真实业务常需同时发起多个独立请求(如查用户、查订单、查积分),再合并结果。CompletableFuture 提供了原生支持,无需手动 await 或锁。
常用组合方式:
- CompletableFuture.allOf():等待全部完成,但不返回结果集合;适合“全成功才继续”的场景,之后需手动 get() 每个 future
- CompletableFuture.anyOf():响应最快的那个,返回其结果(类型为 Object,需强转或统一泛型封装)
-
thenCombine / thenAcceptBoth:将两个已完成的 CompletableFuture 结果按函数合并,例如
userFuture.thenCombine(orderFuture, (u, o) -> new UserOrderDTO(u, o)) - 若需动态数量的任务聚合,可先收集 futures 到 List,再用
allOf(futures.toArray(new CompletableFuture[0]))+ 流式 collect 处理结果
非阻塞的关键:避免 .get(),善用回调与异常机制
调用 get() 会阻塞当前线程,彻底失去异步意义。真正非阻塞的写法,是把后续逻辑作为回调注册进去。
推荐做法:
- 用 exceptionally() 做局部异常兜底,返回默认值,不影响主链执行
- 用 handle() 统一处理成功/失败两种情况,适合记录日志+返回统一响应体
- 用 whenComplete() 执行清理类副作用(如关闭连接、发监控),它不改变结果,也不影响异常传播
- 如必须同步等待(如单元测试),至少加超时:
future.get(3, TimeUnit.SECONDS),防止无限挂起
线程池控制:别让 commonPool 成为性能瓶颈
默认使用 ForkJoinPool.commonPool(),但该池大小等于 CPU 核数,不适合 IO 密集型任务(如 HTTP 调用),易造成线程饥饿。
生产建议:
- IO 型任务(远程调用、DB 查询)务必传入自定义线程池,例如
supplyAsync(() -> api.call(), ioExecutor) - 计算密集型任务可沿用 commonPool,或配置大小为
Runtime.getRuntime().availableProcessors() - 线程池应复用,避免每次 new ThreadPoolExecutor;推荐通过 Spring Bean 或静态 final 实例管理
- 注意:async 后缀方法(如 thenApplyAsync)若未指定 executor,也走 commonPool —— 这点容易被忽略










