外观
一句话答案
CompletableFuture 支持异步编排和组合:supplyAsync→thenApply→exceptionally,比 Future 强大且不阻塞。
核心要点
CompletableFuture vs Future:
Future 的局限:
① get() 阻塞 → 失去异步意义
② 无法组合多个异步任务
③ 无法注册回调(任务完成后自动执行下一步)
CompletableFuture 解决了所有问题 → 非阻塞链式异步编排核心用法:
java
// 1. 创建异步任务
CompletableFuture<String> future = CompletableFuture.supplyAsync(
() -> queryUserFromDB(userId), // 异步执行,返回结果
customExecutor // 可指定线程池(推荐,避免用公共 ForkJoinPool)
);
// 2. 链式转换(thenApply = map)
CompletableFuture<UserDTO> dtoFuture = future
.thenApply(user -> convertToDTO(user)); // 上一步完成后,同步转换
// 3. 链式消费(thenAccept = forEach)
future.thenAccept(user -> log.info("查到用户: {}", user));
// 4. 链式执行(thenRun,不关心上一步结果)
future.thenRun(() -> log.info("任务完成"));
// 5. 异常处理
future.exceptionally(ex -> {
log.error("查询失败", ex);
return defaultUser; // 返回兜底值
});
// 6. handle(同时处理正常结果和异常)
future.handle((result, ex) -> {
if (ex != null) return defaultUser;
return result;
});多任务组合:
java
// 1. 两个任务都完成后合并结果(thenCombine)
CompletableFuture<String> userFuture = supplyAsync(() -> queryUser(id));
CompletableFuture<String> orderFuture = supplyAsync(() -> queryOrder(id));
CompletableFuture<String> combined = userFuture.thenCombine(
orderFuture,
(user, order) -> user + " 的订单: " + order // 两个都完成后合并
);
// 2. 等待所有任务完成(allOf)
CompletableFuture<Void> all = CompletableFuture.allOf(
queryUser(), queryOrder(), queryAddress()
);
all.thenRun(() -> log.info("三个查询全部完成"));
// 3. 任意一个完成即返回(anyOf)
CompletableFuture<Object> fastest = CompletableFuture.anyOf(
queryFromMirror1(), queryFromMirror2(), queryFromMirror3()
);生产实践 — 并行查询多个服务:
java
public UserDetailVO getUserDetail(Long userId) {
CompletableFuture<User> userFuture = supplyAsync(() -> userService.getById(userId), executor);
CompletableFuture<List<Order>> orderFuture = supplyAsync(() -> orderService.listByUserId(userId), executor);
CompletableFuture<Address> addrFuture = supplyAsync(() -> addressService.getByUserId(userId), executor);
CompletableFuture.allOf(userFuture, orderFuture, addrFuture).join();
return new UserDetailVO(userFuture.join(), orderFuture.join(), addrFuture.join());
}
// 三个查询并行执行,总耗时 = max(三个查询) 而不是三个之和注意事项:
① 不要用默认的 ForkJoinPool.commonPool(),应指定业务线程池
② supplyAsync 中的异常不会自动抛出,必须用 exceptionally/handle 处理
③ join() 和 get() 的区别:join() 抛 CompletionException(unchecked),get() 抛 checked exception
④ thenApply vs thenApplyAsync:不带 Async 在上一步的线程中执行,带 Async 提交到线程池追问与易错
追问方向:
- thenApply 和 thenCompose 的区别?
- 异常处理用 exceptionally 还是 handle?
- 不指定线程池有什么问题?
易错点:
- ❌ CompletableFuture 自动处理异常——未捕获的会被静默吞掉
- ❌ 链太长不拆分——影响可读性
💡 记忆锚点
CompletableFuture 是异步任务的流水线:supplyAsync 启动第一道工序,thenApply 接力下一道,exceptionally 兜底次品处理。多条流水线可以用 allOf 等全部完工,也可以用 anyOf 谁先做完用谁的——告别 Future.get() 的"傻等"模式。