中 进阶
CompletableFuture用法#
一句话答案#
CompletableFuture 支持异步编排和组合:supplyAsync→thenApply→exceptionally,比 Future 强大且不阻塞。
核心要点
CompletableFuture vs Future:
Future 的局限:
① get() 阻塞 → 失去异步意义
② 无法组合多个异步任务
③ 无法注册回调(任务完成后自动执行下一步)
CompletableFuture 解决了所有问题 → 非阻塞链式异步编排plaintext核心用法:
// 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(三个查询) 而不是三个之和java注意事项:
① 不要用默认的 ForkJoinPool.commonPool(),应指定业务线程池
② supplyAsync 中的异常不会自动抛出,必须用 exceptionally/handle 处理
③ join() 和 get() 的区别:join() 抛 CompletionException(unchecked),get() 抛 checked exception
④ thenApply vs thenApplyAsync:不带 Async 在上一步的线程中执行,带 Async 提交到线程池plaintext面试回答(2分钟版)
CompletableFuture 是 Java 8 引入的异步编排工具,解决了 Future.get() 阻塞等待的问题。它的核心能力有三个:第一是链式异步处理,supplyAsync 启动异步任务,thenApply 对结果做转换,thenAccept 消费结果,exceptionally 做异常兜底,整条链非阻塞无需手动 get;第二是多任务组合,allOf 等待所有任务完成、anyOf 取最先完成的结果、thenCombine 合并两个任务的结果;第三是灵活的异常处理,handle 方法可以同时处理正常结果和异常。生产中最典型的用法是并行查询多个服务:用户详情页需要同时查用户信息、订单列表、收货地址,三个 supplyAsync 并行执行后用 allOf 等待全部完成,总耗时等于最慢的那个查询而不是三者之和。关键注意点:一定要指定自定义线程池而不是默认的 ForkJoinPool.commonPool(),因为公共池被一个慢任务阻塞会影响全局;异常不会自动抛出,必须用 exceptionally 或 handle 显式处理。
追问与易错
追问方向:
- “thenApply 和 thenCompose 的区别?”→ thenApply 是 map(同步转换值),thenCompose 是 flatMap(返回新的 CompletableFuture 避免嵌套),类似 Stream 的 map vs flatMap
- “异常处理用 exceptionally 还是 handle?”→ exceptionally 只处理异常返回兜底值,handle 同时处理正常结果和异常,需要根据是否要区分正常/异常逻辑来选择
- “不指定线程池有什么问题?”→ 默认使用 ForkJoinPool.commonPool(),该池全 JVM 共享,一个慢任务阻塞会影响所有使用公共池的操作,生产中必须指定独立业务线程池
易错点:
- ❌ CompletableFuture 自动处理异常——未捕获的会被静默吞掉
- ❌ 链太长不拆分——影响可读性