CompletableFuture 是 Java 8 提供的异步编程核心工具,支持链式调用、线程池可控、组合灵活;需显式指定线程池,区分 thenApply/thenAccept/thenRun,善用 thenCompose/thenCombine 组合任务,并通过 exceptionally/handle 健壮处理异常。

CompletableFuture 是 Java 8 引入的异步编程核心工具,它把“启动任务→等待结果→处理结果→应对异常”这一整套流程,从传统阻塞式、嵌套回调式,变成可读性强、组合灵活、线程可控的链式表达。用对了,代码既简洁又健壮;用错了,容易线程耗尽、异常丢失或逻辑错乱。
创建异步任务:选对方法,指定线程池
别依赖默认线程池——ForkJoinPool.commonPool() 是全局共享的,高并发下极易成为瓶颈。生产环境务必显式传入自定义线程池。
-
有返回值任务:用
supplyAsync(Supplier, Executor)
例如查数据库、调远程接口后返回对象:CompletableFuture<User> userFut = CompletableFuture.supplyAsync(() -> userDao.findById(123), dbExecutor); -
无返回值任务:用
runAsync(Runnable, Executor)
例如发通知、写日志、刷新缓存:CompletableFuture.runAsync(() -> notifyService.send("order_created"), mqExecutor); -
已知结果,直接完成:用
completedFuture(T)或手动new CompletableFuture<>().complete(...)
适合兜底、mock、短路逻辑,避免无谓异步开销。
处理结果:分清 thenApply / thenAccept / thenRun
三者都表示“前一个任务完成后执行”,但语义和用途完全不同,混用会导致编译失败或逻辑中断:
- thenApply:接收上一结果,返回新值 → 用于转换(如 String → Integer、DTO → VO),后续可继续链式调用;
- thenAccept:接收上一结果,不返回值 → 用于消费(如打印、入库、发消息),不能再接 thenApply;
- thenRun:不接收结果,也不返回值 → 仅做收尾动作(如清理资源、记录完成时间),常用于无数据依赖的旁路操作。
示例:
supplyAsync(...)<br> .thenApply(s -> s.toUpperCase())<br> .thenAccept(System.out::println)<br> .thenRun(() -> log.info("done"));
立即学习“Java免费学习笔记(深入)”;
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
组合多个异步任务:串行用 thenCompose,并行用 thenCombine
这是 CompletableFuture 区别于普通 Future 的关键能力:
-
thenCompose:前一个结果是下一个异步任务的输入,形成「异步流水线」
比如:查用户 → 根据 userId 查订单 → 合并成用户订单视图;getUser().thenCompose(user -> getOrderList(user.getId())) -
thenCombine:两个独立异步任务并行执行,都完成后合并结果
比如:查用户信息 + 查用户积分,再一起组装响应;userFut.thenCombine(pointsFut, (u, p) -> new UserProfile(u, p)) - 补充:多个任务全部完成用
CompletableFuture.allOf(...)(返回 void),全部结果收集用CompletableFuture.allOf(...).thenApply(v -> ...)配合future.join()拆包。
异常处理:别让异常静默吞掉
get() 和 join() 抛异常会中断链路;必须用专门方法捕获并恢复流程:
- exceptionally(Function):只在异常时触发,返回替代值(如兜底数据、空对象);
- handle(BiFunction):无论成功或异常都会执行,统一收口处理(适合打日志+返回默认值);
- 注意:
whenComplete只做副作用(如清理),不改变结果值,也不能中断异常传播。
推荐模式:
supplyAsync(...)<br> .thenApply(...)<br> .exceptionally(ex -> {<br> log.error("fallback due to", ex);<br> return getDefaultData();<br> });
获取结果:避免在线程敏感场景用 get()
除非你明确需要同步阻塞(如单元测试、命令行工具),否则尽量用 join() 替代 get():
-
join()抛出的是运行时异常(CompletionException),无需声明 throws,更符合函数式风格; -
get()抛出InterruptedException和ExecutionException,强制 try-catch,且可能被中断影响主线程; - 真正需要超时控制时,才用
get(long, TimeUnit),但应配合isDone()或completeOnTimeout()做防御。


















