Future 表示一个尚未完成的结果,CompletableFuture 还能声明结果之间的依赖。异步编排必须同时设计执行器、截止时间、
异常语义和取消传播;只把同步调用包进 supplyAsync 不会自动获得可靠性。
Table of contents
Open Table of contents
运行组合示例
下载CompletableFuture 样例下载, 运行:
java -cp target/classes dev.elaine.concurrency.CompletableFutureSample
程序使用显式固定线程池,组合字符串转换,设置 1 秒超时,并在失败时返回降级结果。正常输出为 你好,并发学习者。
thenApply 与 thenCompose
thenApply 转换普通结果;回调返回另一个异步阶段时使用 thenCompose 展平:
CompletableFuture<User> user = loadUser(id);
CompletableFuture<List<Order>> orders = user.thenCompose(value -> loadOrders(value.id()));
若使用 thenApply(value -> loadOrders(...)),类型会变成嵌套的 CompletableFuture<CompletableFuture<...>>。
独立任务可用 thenCombine 合并,但要考虑任一任务失败时另一个是否应该取消。
非 async 回调在哪个线程执行
非 Async 方法的回调可能由完成上一步的线程执行,也可能由触发完成的调用方执行。没有显式 Executor 的 Async 方法
通常使用 ForkJoinPool.commonPool()。因此不要在未知完成线程中执行长时间阻塞、访问线程上下文或持有框架锁。
关键业务使用独立、命名、有界的 Executor,并在每个异步边界显式传入。
超时不等于停止底层工作
orTimeout 会让 CompletableFuture 异常完成,但不保证正在执行的数据库或网络调用立即停止。底层客户端仍需连接、读取和
整体截止时间;取消也要传递到实际资源。
CompletableFuture.cancel 被视为异常完成。它不像 FutureTask 那样直接控制产生结果的计算,mayInterruptIfRunning
不应被当作可靠的强制中断协议。Java 25 的
CompletableFuture API
明确说明了这种取消语义。
异常处理不要吞根因
exceptionally 只适合明确的降级。需要记录成功或失败但保持结果时使用 whenComplete;需要把成功和失败都转换为新结果时
使用 handle。日志应保留根因和请求标识,但不能泄露 Token 或个人数据。
跨线程时 ThreadLocal 不会自动传播。显式传递不可变 Context,或使用框架提供且经过清理验证的上下文传播机制。
下一步
阅读ForkJoinPool、工作窃取与并行流,判断 CPU 密集任务何时值得 递归拆分。