completablefuture 通过 thenapply、thencompose 等方法建立任务依赖实现逻辑串行,而非阻塞等待;thenapply 用于纯计算链式转换,thencompose 扁平化嵌套异步调用,thenaccept/thenrun 处理无返回值操作,线程池可显式控制物理串行。

在 Java 多线程中,CompletableFuture 本身是为异步、非阻塞、可组合的并发编程设计的,它默认不强制串行;但可以通过链式调用和依赖关系,让多个任务按顺序执行(即前一个完成后再触发下一个),实现逻辑上的“串行”。关键不是阻塞等待,而是用 thenApply、thenAccept、thenCompose 等方法建立任务间的先后依赖。
用 thenApply 实现纯计算型任务串行
适用于返回结果、无副作用的函数式处理。每个阶段接收上一阶段的结果,并返回新结果,整个链条自动按序执行。
- 第一个任务用
supplyAsync()启动异步计算 - 后续用
thenApply()接续处理,它会在前一个CompletableFuture完成后立即在同一个线程(或默认 ForkJoinPool 线程)中执行 - 所有阶段默认在同一个异步上下文中串行流转,无需手动 await 或 join
示例:
CompletableFuture<string> result = CompletableFuture
.supplyAsync(() -> "hello")
.thenApply(s -> s.toUpperCase())
.thenApply(s -> s + " world");
// 最终 result.get() 返回 "HELLO world"</string>
用 thenCompose 实现含异步操作的串行
当某个中间步骤本身也返回一个新的 CompletableFuture(比如调用另一个异步接口),必须用 thenCompose 而不是 thenApply,否则会嵌套 CompletableFuture<completablefuture>></completablefuture>。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
-
thenCompose会“扁平化”嵌套,把两个异步阶段合并为一个连续的异步流 - 适合模拟多步远程调用:查用户 → 根据 ID 查订单 → 根据订单查物流
- 每个环节都异步,但整体逻辑严格串行
示例:
CompletableFuture<user> userFuture = CompletableFuture.supplyAsync(() -> findUser(123));
CompletableFuture<order> orderFuture = userFuture
.thenCompose(user -> CompletableFuture.supplyAsync(() -> findOrder(user.getId())));
CompletableFuture<tracking> trackingFuture = orderFuture
.thenCompose(order -> CompletableFuture.supplyAsync(() -> findTracking(order.getId())));</tracking></order></user>
用 thenAccept/thenRun 处理无返回值的串行动作
当某一步只做事情(如写日志、发通知、更新缓存),不产生新值,可用 thenAccept(接收参数)或 thenRun(无参)。
-
thenAccept拿到上游结果,执行消费逻辑,不改变结果类型 - 多个
thenAccept可依次追加,形成“执行→记录→清理”的串行流程 - 注意:这些方法返回的是
CompletableFuture<void></void>,若需继续链式,最后可接thenApply(v -> null)或直接结束
控制执行线程以避免意外并行
默认情况下,thenApply 等方法可能复用前一个任务的线程(如果前一个已结束),也可能切换到 ForkJoinPool 线程;若需明确串行且可控,建议显式指定线程池。
- 用
thenApplyAsync(fn, executor)指定统一的单线程池,可确保物理串行(但一般不必要,逻辑串行已足够) - 例如:
Executors.newSingleThreadExecutor()配合thenApplyAsync,能强制所有阶段在同一个线程顺序执行 - 不过要注意:单线程池会成为瓶颈,仅在调试、顺序敏感场景(如状态机推进)下谨慎使用
不复杂但容易忽略:串行的关键在于任务之间的依赖声明,而不是线程数量。只要用对了 thenXXX 方法,CompletableFuture 就会自动保障执行顺序,无需 sleep、wait 或手动 join。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










