CompletableFuture 编排异步任务的核心是串行(thenApply/thenAccept/thenRun)、并行(thenCombine/thenAcceptBoth)、聚合(allOf/anyOf)和异常处理(exceptionally/handle),并需注意自定义线程池。

Java 中用 CompletableFuture 编排异步任务,核心是把多个异步操作串起来、并起来或组合起来,同时避免阻塞主线程。它比传统 Future 更灵活,支持链式调用、异常处理、依赖触发等能力。
串行执行:用 thenApply / thenAccept / thenRun 传递结果
适合前一个任务的结果要作为后一个任务的输入的场景。注意这些方法都返回新的 CompletableFuture,支持链式调用。
-
thenApply:接收上一阶段结果,返回新值(有返回值) -
thenAccept:接收结果但不返回(消费型,如打印日志) -
thenRun:不依赖上一阶段结果,只在完成后执行(无参)
示例:
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenAccept(System.out::println); // 输出:Hello World
并行执行:用 thenCombine / thenAcceptBoth 合并两个异步结果
当两个独立异步任务都完成,需要把它们的结果一起处理时使用。
-
thenCombine:合并两个CompletableFuture的结果,返回新值 -
thenAcceptBoth:合并结果但不返回,只做消费操作
示例:
CompletableFuture<string> task1 = CompletableFuture.supplyAsync(() -> "Java");
CompletableFuture<integer> task2 = CompletableFuture.supplyAsync(() -> 17);
task1.thenCombine(task2, (s, i) -> s + " v" + i)
.thenAccept(System.out::println); // 输出:Java v17</integer></string>
等待全部完成:用 allOf / anyOf 统一协调多个任务
allOf 等待所有任务完成(不直接返回结果,需手动获取),anyOf 只等任意一个完成(返回第一个完成的 Object,需类型转换)。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
- 用
allOf后接join()+get()提取各任务结果 - 推荐对每个子任务单独
join(),避免allOf().join()后再调get()抛异常难定位
示例(收集多个异步结果):
CompletableFuture<string> a = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<string> b = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<string> c = CompletableFuture.supplyAsync(() -> "C");
CompletableFuture.allOf(a, b, c).join(); // 等全部结束
List<string> results = Arrays.asList(
a.join(), b.join(), c.join()
); // ["A", "B", "C"]</string></string></string></string>
异常处理:用 exceptionally / handle 统一兜底
异步链中任一环节抛异常,会跳过后续 then*,直接走 exceptionally 或 handle。
-
exceptionally:只在异常时执行,返回默认值(类型需匹配) -
handle:无论成功失败都执行,参数为 (result, throwable),更灵活
示例:
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Oops");
})
.thenApply(s -> s.length())
.exceptionally(t -> -1) // 返回 -1
.thenAccept(System.out::println); // 输出:-1
不复杂但容易忽略:线程池选择很重要。默认用 ForkJoinPool.commonPool(),生产环境建议传入自定义线程池,尤其涉及 I/O 时,避免阻塞公共池。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










