软件技术 · 知识点深化 · 并发编程
异步编程与 CompletableFuture:从 Future 到链式编排
JDK8 的 CompletableFuture 把异步编程从" future.get() 死等"解放成链式编排:thenApply 转换、thenCombine 合并、thenCompose 扁平化、exceptionally 兜底。这一页把异步回调地狱写成优雅流水线,面试手撕 CompletableFuture。
① 怎么学(4 步走,约 65 分钟)
先建立直觉再抠细节,按这四步走最稳:
1看图建立直觉(10 分钟)
读②③:先在脑子里画出本课核心结构图。
2记完整体系(15 分钟)
读④:把对比表和公式看懂,不要急着背。
3跟例题走一遍(20 分钟)
精读⑤:看三个例题怎么用知识点解题。
4刷题纠错(剩余时间)
做⑦⑩,错题回⑥诊断。
本课小目标学完你要能:① 对比 Future 与 CompletableFuture;② 写出链式编排代码;③ 区分 thenApply/thenCompose/thenCombine;④ 处理异步异常。
② 一图看懂:CompletableFuture 方法全景
读法:中心是本课主题,四条分支展开核心维度,下方是典型应用与高频易错点。
③ 本质直觉:CompletableFuture 就是"外卖流水线"
传统 Future 像点外卖后一直盯着手机等(future.get() 阻塞)。CompletableFuture 像餐厅后厨流水线:切菜→炒菜→装盘→上菜,每一步做完自动交给下一步,不用人盯着。
supplyAsync:开一个异步任务去做菜。
thenApply:菜做好后加工(转换结果)。
thenCombine:两个菜都做好后拼成套餐。
thenCompose:第一个菜做好后,用它去点第二个菜(嵌套拍平)。
exceptionally:菜糊了换个方案。
同步 vs 异步版本:thenApply 用调用者线程执行;thenApplyAsync 丢给线程池。默认用 ForkJoinPool.commonPool()。
Future 的局限Future.get() 阻塞、不能链式调用、不能组合多个 future、异常处理麻烦。CompletableFuture 全部解决。
④ 完整知识体系
核心方法分类
| 方法 | 作用 | 类比 |
| supplyAsync | 异步有返回值 | 下单做菜 |
| runAsync | 异步无返回值 | 叫号 |
| thenApply | 同步转换结果 | 加工菜 |
| thenApplyAsync | 异步转换 | 丢给别的档口 |
| thenCombine | 合并两个 future | 两个菜拼套餐 |
| thenCompose | 嵌套 future 拍平 | 用第一道菜再点第二道 |
| exceptionally | 异常兜底 | 菜糊了换菜 |
| allOf | 等全部完成 | 等所有菜齐 |
thenApply vs thenComposethenApply 是 T->R;thenCompose 是 T->Future,拍平嵌套
allOf 用法CompletableFuture.allOf(f1,f2,f3).join() 等全部完成
异常处理
exceptionally:出异常时返回默认值。
handle:无论正常还是异常都执行,能拿到结果或异常。
whenComplete:消费结果但不改变结果。
线程池建议默认 ForkJoinPool.commonPool() 适合 CPU 密集;IO 密集(调远程服务)必须传自定义线程池,否则公共池被阻塞打满。
⑤ 应用场景与例题
例1 并行查用户信息和订单
两个服务并行调用,总耗时=max(两个)。
CompletableFuture f1 = supplyAsync(()->userService.get(id)); CompletableFuture f2 = supplyAsync(()->orderService.get(id)); 用 thenCombine 合并结果,总耗时≈较慢那个,而非两者之和。
例2 thenApply 和 thenCompose 区别
为什么需要 thenCompose?
thenApply 返回值会被自动包装成 CompletableFuture,嵌套两层。thenCompose 像 flatMap,把嵌套拍平。如果第二步本身返回 CompletableFuture,用 thenCompose。
例3 allOf 等三个任务完成
三个并行任务都完成后汇总。
CompletableFuture.allOf(f1,f2,f3).join(); 然后用 f1.get() 等取结果。注意 allOf 本身不返回结果,要手动取。
做题心法链式调用的关键:每一步返回新的 CompletableFuture,像流水线一样串起来。
⑥ 高频错误诊断(4 条)
错误1:异步任务里调用 get()get() 阻塞,失去异步意义。用 thenApply 回调。
错误2:用默认线程池跑 IO 任务commonPool 被阻塞 IO 打满,必须传自定义线程池。
错误3:thenApply 和 thenCompose 混用返回 CompletableFuture 的函数必须用 thenCompose,否则嵌套。
错误4:异常没人处理异步任务异常会被吞掉,必须 exceptionally 或 handle 兜底。
⑦ 考点真题演练(5 题)
考点分布
| 考法 | 出题形式 | 应对 |
| 方法 | 问 thenApply vs thenCompose | 后者拍平嵌套 |
| 创建 | 问 supplyAsync 作用 | 异步有返回值 |
| 合并 | 问 allOf 语义 | 等全部完成 |
| 线程池 | 问为什么要传自定义池 | IO 阻塞 commonPool |
真题basic1. CompletableFuture.supplyAsync 的作用是?
真题mid2. thenCompose 相对于 thenApply 类似 Stream 的哪个操作?
真题mid3. CompletableFuture.allOf(f1,f2) 的作用是?
真题hard4. IO 密集异步任务为什么要传自定义线程池?
真题hard5. 异步任务异常默认会怎样?
⑧ 必背知识点卡
创建:supplyAsync 有返回,runAsync 无返回
转换:thenApply 同步,thenApplyAsync 异步
拍平:thenCompose = flatMap
合并:thenCombine 两个 future
全等:allOf 等全部,anyOf 等任一
异常:exceptionally 兜底,handle 都处理
线程池:IO 任务必须传自定义池 别用 commonPool
⑨ 动手输出:用 CompletableFuture 优化订单详情页
场景:订单页要查用户、商品、库存、优惠券 4 个服务,串行要 800ms。
① 现状:4 个服务串行调用,总耗时=4×200=800ms。
② 改造:用 supplyAsync 并行发起 4 个请求,各占一个线程。
③ 合并:allOf 等 4 个都完成,然后汇总成页面 VO。
④ 耗时:≈ max(200ms)=200ms,快 4 倍。
⑤ 兜底:任何一个服务挂了,exceptionally 返回降级数据(如用户信息查不到用默认头像)。
口述思路"能并行就别串行,CompletableFuture 让并行像写同步代码一样自然。"
⑩ 分层练习(基础 + 中档 + 拔高)
▍基础 6 题
基础1CompletableFuture 哪个方法异步有返回值?
supplyAsync。
基础2thenApply 和 thenApplyAsync 区别?
前者同步执行,后者丢线程池。
基础3allOf 的作用?
等所有 future 完成。
基础4异常用哪个方法兜底?
exceptionally。
基础5thenCompose 类似 Stream 的什么?
flatMap。
基础6默认线程池是哪个?
ForkJoinPool.commonPool()。
▍中档 5 题
中档1Future 的主要缺点?
get 阻塞、不能链式、不能组合、异常难处理。
中档2thenCombine 和 thenCompose 区别?
Combine 合并两个独立 future;Compose 是嵌套 future 拍平。
中档3为什么 IO 任务不能用 commonPool?
commonPool 线程数=cpu核数,IO 阻塞会占满。
中档4handle 和 exceptionally 区别?
handle 正常异常都处理;exceptionally 只处理异常。
中档5whenComplete 的作用?
消费结果但不改变结果,类似 peek。
▍拔高 5 题
拔高1allOf 为什么不直接返回结果?
它是 Void,需要手动 get 各个 future。
拔高2anyOf 适合什么场景?
多个冗余服务调用,谁先回用谁。
拔高3CompletableFuture 线程不结束怎么办?
非守护线程会阻塞 JVM 退出,用 commonPool 守护线程避免。
拔高4怎么设置异步任务超时?
orTimeout() 或 get(timeout, unit)。
拔高5thenApply 用哪个线程执行?
默认由完成它的线程执行(可能是调用线程或异步线程)。
⑪ 记忆口诀 + 7 天复习计划
三句口诀① supplyAsync 开任务,thenApply 串转换。② thenCompose 拍平,thenCombine 合并。③ IO 必须传线程池,异常兜底 exceptionally。
| 天 | 任务 | 自检 |
| 第 1 天 | 读②③,画方法分类图 | 能分类 |
| 第 2 天 | 背方法表 + 基础 6 题 | 方法名默写 |
| 第 3 天 | 做中档 5 题,写并行调用代码 | 代码能跑 |
| 第 4 天 | 做拔高 5 题,加超时和降级 | 完整可用 |
| 第 5 天 | 做⑦真题 5 题 | 限时每题 2 分钟 |
| 第 6-7 天 | 合书口述 Future 缺点 | 不看资料 |
← 返回软件技术总览