从回调地狱到链式优雅,CompletableFuture让异步编程像写诗一样!
一、开场:异步编程的痛点😫
远古时代:回调地狱
// 场景:下单 → 扣库存 → 扣积分 → 发短信
public void placeOrder(Order order) {
new Thread(() -> {
// 第一步:扣库存
deductStock(order, (stockResult) -> {
if (stockResult.isSuccess()) {
// 第二步:扣积分
new Thread(() -> {
deductPoints(order, (pointsResult) -> {
if (pointsResult.isSuccess()) {
// 第三步:发短信
new Thread(() -> {
sendSMS(order, (smsResult) -> {
// 终于结束了!😭
System.out.println("订单完成");
});
}).start();
}
});
}).start();
}
});
}).start();
}
问题:
- 嵌套太深,像俄罗斯套娃🪆
- 异常处理困难
- 代码难以维护
现代解决方案:CompletableFuture
public CompletableFuture<Void> placeOrder(Order order) {
return CompletableFuture
.supplyAsync(() -> deductStock(order)) // 扣库存
.thenCompose(stock -> deductPoints(order)) // 扣积分
.thenCompose(points -> sendSMS(order)) // 发短信
.thenAccept(sms -> System.out.println("订单完成"))
.exceptionally(ex -> {
log.error("订单失败", ex);
return null;
});
}
优势:
- 链式调用,清晰流畅✨
- 统一的异常处理
- 代码优雅易读
二、核心概念:Future的进化史📜
阶段1:Future(JDK 5)
ExecutorService executor = Executors.newFixedThreadPool(10);
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Hello";
});
String result = future.get(); // ❌ 阻塞等待
问题:
get()会阻塞- 无法链式调用
- 没有异常处理机制
- 无法组合多个Future
阶段2:CompletableFuture(JDK 8)⭐
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> "Hello")
.thenApply(s -> s + " World")
.thenApply(String::toUpperCase);
future.thenAccept(System.out::println); // 非阻塞
新特性:
- ✅ 支持链式调用
- ✅ 非阻塞回调
- ✅ 异常处理
- ✅ 多任务组合
三、创建CompletableFuture的4种方式🎯
方式1:runAsync(无返回值)
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("执行异步任务");
// 无返回值
});
生活类比: 叫外卖🍕,你不关心谁送的,只要送到就行。
方式2:supplyAsync(有返回值)
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 执行耗时操作
return "结果数据";
});
生活类比: 网购📦,你要收到商品(返回值)。
方式3:completedFuture(已完成)
CompletableFuture<String> future = CompletableFuture.completedFuture("立即返回");
生活类比: 从缓存中取数据,秒返!
方式4:手动创建并完成
CompletableFuture<String> future = new CompletableFuture<>();
// 在某个时刻完成
new Thread(() -> {
try {
Thread.sleep(1000);
future.complete("手动完成"); // 正常完成
// 或 future.completeExceptionally(new Exception()); // 异常完成
} catch (Exception e) {
future.completeExceptionally(e);
}
}).start();
四、链式调用:12大操作符🔗
1. thenApply - 转换结果
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> "123")
.thenApply(Integer::parseInt) // String → Integer
.thenApply(num -> num * 2); // 123 → 246
类比: 流水线加工🏭,每一步都改变产品形态。
2. thenAccept - 消费结果(无返回)
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(result -> System.out.println(result)); // 打印,不返回
类比: 快递到了签收📝,不需要回执。
3. thenRun - 执行后续操作(不接收结果)
CompletableFuture.supplyAsync(() -> "Hello")
.thenRun(() -> System.out.println("任务完成")); // 不关心上一步结果
类比: 做完作业就去玩,作业内容不重要。
4. thenCompose - 扁平化嵌套Future
CompletableFuture<User> future = CompletableFuture
.supplyAsync(() -> getUserId())
.thenCompose(userId -> CompletableFuture.supplyAsync(() -> queryUser(userId)));
类比: 先查ID,再根据ID查详情,避免Future嵌套。
5. thenCombine - 合并两个Future
CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> result = future1.thenCombine(future2, (a, b) -> a + b);
// 结果:30
类比: 你和朋友分别点外卖,都到了一起吃🍜🍕。
6. thenAcceptBoth - 消费两个结果
future1.thenAcceptBoth(future2, (result1, result2) -> {
System.out.println(result1 + result2);
});
7. runAfterBoth - 两个都完成后执行
future1.runAfterBoth(future2, () -> {
System.out.println("两个任务都完成了");
});
8. applyToEither - 谁快用谁
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
sleep(100);
return "淘宝";
});
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
sleep(200);
return "京东";
});
CompletableFuture<String> result = future1.applyToEither(future2, s -> s);
// 结果:淘宝(更快)
类比: 同时叫两辆出租车🚕🚕,谁先到坐谁的。
9. acceptEither - 消费最快的结果
future1.acceptEither(future2, result -> {
System.out.println("最快的结果:" + result);
});
10. runAfterEither - 任一完成就执行
future1.runAfterEither(future2, () -> {
System.out.println("有一个完成了");
});
11. exceptionally - 异常处理
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) {
throw new RuntimeException("出错了");
}
return "成功";
}).exceptionally(ex -> {
log.error("异常", ex);
return "默认值"; // 返回降级值
});
类比: 备胎方案,挂了就用Plan B。
12. handle - 统一处理结果和异常
CompletableFuture.supplyAsync(() -> "Hello")
.handle((result, ex) -> {
if (ex != null) {
return "出错了: " + ex.getMessage();
}
return result.toUpperCase();
});
五、组合多个Future:allOf & anyOf🎭
allOf - 等待所有任务完成
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "任务1");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "任务2");
CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> "任务3");
CompletableFuture<Void> allFuture = CompletableFuture.allOf(future1, future2, future3);
allFuture.thenRun(() -> {
// 所有任务都完成了
System.out.println("全部完成");
});
实战:并行查询多个数据源
public List<Product> searchProducts(String keyword) {
CompletableFuture<List<Product>> taobao =
CompletableFuture.supplyAsync(() -> searchTaobao(keyword));
CompletableFuture<List<Product>> jd =
CompletableFuture.supplyAsync(() -> searchJD(keyword));
CompletableFuture<List<Product>> pdd =
CompletableFuture.supplyAsync(() -> searchPDD(keyword));
// 等待所有完成
CompletableFuture.allOf(taobao, jd, pdd).join();
// 合并结果
List<Product> result = new ArrayList<>();
result.addAll(taobao.join());
result.addAll(jd.join());
result.addAll(pdd.join());
return result;
}
anyOf - 任一完成即返回
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2, future3);
anyFuture.thenAccept(result -> {
System.out.println("最快的结果:" + result);
});
实战:多服务容灾
public String getUserInfo(Long userId) {
CompletableFuture<String> master =
CompletableFuture.supplyAsync(() -> queryFromMaster(userId));
CompletableFuture<String> slave1 =
CompletableFuture.supplyAsync(() -> queryFromSlave1(userId));
CompletableFuture<String> slave2 =
CompletableFuture.supplyAsync(() -> queryFromSlave2(userId));
// 谁快用谁
return (String) CompletableFuture.anyOf(master, slave1, slave2).join();
}
六、线程池选择:别踩坑!⚠️
默认线程池(不推荐)
CompletableFuture.supplyAsync(() -> {
// 使用ForkJoinPool.commonPool()
return "Hello";
});
问题:
- 所有异步任务共享一个线程池
- 线程数 = CPU核心数
- IO密集任务会阻塞其他任务
自定义线程池(推荐)✅
// 创建专用线程池
ExecutorService executor = new ThreadPoolExecutor(
10, // 核心线程数
50, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲存活时间
new LinkedBlockingQueue<>(1000),
new ThreadFactoryBuilder().setNameFormat("async-pool-%d").build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
// 使用自定义线程池
CompletableFuture.supplyAsync(() -> {
return queryDatabase();
}, executor); // 指定线程池
最佳实践:
- CPU密集型: 线程数 = CPU核心数 + 1
- IO密集型: 线程数 = CPU核心数 * 2(或更多)
- 不同类型任务用不同线程池
七、实战案例:电商商品详情页💰
@Service
public class ProductDetailService {
@Autowired
private ExecutorService asyncExecutor;
/**
* 查询商品详情(并行查询多个数据)
*/
public ProductDetailVO getProductDetail(Long productId) {
// 1. 查询基本信息
CompletableFuture<Product> productFuture = CompletableFuture
.supplyAsync(() -> productMapper.selectById(productId), asyncExecutor);
// 2. 查询库存
CompletableFuture<Stock> stockFuture = CompletableFuture
.supplyAsync(() -> stockService.getStock(productId), asyncExecutor);
// 3. 查询价格
CompletableFuture<Price> priceFuture = CompletableFuture
.supplyAsync(() -> priceService.getPrice(productId), asyncExecutor);
// 4. 查询评论摘要
CompletableFuture<CommentSummary> commentFuture = CompletableFuture
.supplyAsync(() -> commentService.getSummary(productId), asyncExecutor);
// 5. 查询推荐商品
CompletableFuture<List<Product>> recommendFuture = CompletableFuture
.supplyAsync(() -> recommendService.getRecommend(productId), asyncExecutor);
// 6. 等待所有任务完成
CompletableFuture<Void> allFuture = CompletableFuture.allOf(
productFuture, stockFuture, priceFuture, commentFuture, recommendFuture
);
// 7. 组装结果
return allFuture.thenApply(v -> {
ProductDetailVO vo = new ProductDetailVO();
vo.setProduct(productFuture.join());
vo.setStock(stockFuture.join());
vo.setPrice(priceFuture.join());
vo.setCommentSummary(commentFuture.join());
vo.setRecommendProducts(recommendFuture.join());
return vo;
}).exceptionally(ex -> {
log.error("查询商品详情失败", ex);
return getDefaultDetail(productId); // 降级方案
}).join();
}
}
性能对比:
| 方式 | 耗时 |
|---|---|
| 串行查询 | 500ms + 200ms + 100ms + 300ms + 400ms = 1500ms |
| CompletableFuture并行 | max(500, 200, 100, 300, 400) = 500ms |
**性能提升:3倍!**🚀
八、异常处理的3种姿势🛡️
方式1:exceptionally(推荐)
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("失败");
return "成功";
}).exceptionally(ex -> {
log.error("异常", ex);
return "默认值";
});
方式2:handle(更灵活)
CompletableFuture.supplyAsync(() -> "Hello")
.handle((result, ex) -> {
if (ex != null) {
return "出错: " + ex.getMessage();
}
return result.toUpperCase();
});
方式3:whenComplete(不改变结果)
CompletableFuture.supplyAsync(() -> "Hello")
.whenComplete((result, ex) -> {
if (ex != null) {
log.error("异常", ex);
} else {
log.info("成功:{}", result);
}
// 不能改变结果,只能记录日志
});
九、常见陷阱与最佳实践⚠️
陷阱1:忘记指定线程池
// ❌ 使用默认ForkJoinPool
CompletableFuture.supplyAsync(() -> blockingIO());
// ✅ 指定专用线程池
CompletableFuture.supplyAsync(() -> blockingIO(), ioExecutor);
陷阱2:阻塞等待join()
// ❌ 在主线程阻塞
String result = CompletableFuture.supplyAsync(() -> "Hello").join();
// ✅ 用回调处理结果
CompletableFuture.supplyAsync(() -> "Hello")
.thenAccept(result -> handleResult(result));
陷阱3:忘记异常处理
// ❌ 异常被吞掉
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("错误");
});
// ✅ 添加异常处理
CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("错误");
}).exceptionally(ex -> {
log.error("异常", ex);
return null;
});
陷阱4:链式调用中断
// ❌ thenApply返回null会中断
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> null) // 中断了!
.thenAccept(System.out::println); // 不会执行
// ✅ 避免返回null
CompletableFuture.supplyAsync(() -> "Hello")
.thenApply(s -> s == null ? "" : s)
.thenAccept(System.out::println);
十、超时控制⏰
try {
String result = CompletableFuture
.supplyAsync(() -> slowMethod())
.orTimeout(3, TimeUnit.SECONDS) // JDK 9+
.get();
} catch (TimeoutException e) {
System.out.println("超时了");
}
// 或者使用completeOnTimeout提供默认值
String result = CompletableFuture
.supplyAsync(() -> slowMethod())
.completeOnTimeout("默认值", 3, TimeUnit.SECONDS) // JDK 9+
.join();
十一、总结:CompletableFuture最佳实践📝
✅ DO
- 自定义线程池,避免用默认的
- 异常处理,用exceptionally或handle
- 非阻塞回调,避免用join()/get()阻塞
- 合理组合,用allOf/anyOf提升性能
- 设置超时,防止永久等待
❌ DON'T
- ❌ 不要在CompletableFuture中阻塞
- ❌ 不要忘记异常处理
- ❌ 不要过度使用,简单场景用Stream
- ❌ 不要忽略线程池配置
- ❌ 不要在回调中抛出异常而不处理
十二、面试高频问答💯
Q1: CompletableFuture和Future的区别?
A:
- Future只能get()阻塞等待
- CompletableFuture支持链式调用、回调、组合
- CompletableFuture可以手动完成
Q2: thenApply和thenCompose的区别?
A:
thenApply:转换值,返回CompletableFuture<U>thenCompose:扁平化,避免CompletableFuture<CompletableFuture<U>>嵌套
Q3: 如何取消一个CompletableFuture?
A: 调用cancel(true),但无法中断正在执行的任务,只能设置为取消状态。
Q4: CompletableFuture的性能如何?
A: 轻量级,性能接近原生线程池,但要注意线程池配置和避免阻塞。
下期预告: Disruptor如何做到比JDK队列快10倍?无锁的秘密!🔐