🏭 P10: 生产场景 — 异步改造实战
🛒 场景1:下单接口太慢 — 串行→并行
// ❌ 改造前:3个查询串行执行,总耗时 450ms
@PostMapping("/order")
public OrderVO createOrder(CreateOrderReq req) {
String stock = stockService.check(req.getProductId()); // 200ms
String coupon = couponService.check(req.getUserId()); // 150ms
String address = addressService.get(req.getUserId()); // 100ms
// 总耗时:200+150+100 = 450ms
return buildOrderVO(stock, coupon, address);
}
// ✅ 改造后:3个查询并行,总耗时 200ms
@PostMapping("/order")
public OrderVO createOrder(CreateOrderReq req) {
CompletableFuture<String> stockFuture = CompletableFuture
.supplyAsync(() -> stockService.check(req.getProductId()), bizPool);
CompletableFuture<String> couponFuture = CompletableFuture
.supplyAsync(() -> couponService.check(req.getUserId()), bizPool);
CompletableFuture<String> addressFuture = CompletableFuture
.supplyAsync(() -> addressService.get(req.getUserId()), bizPool);
// 等待全部完成
CompletableFuture.allOf(stockFuture, couponFuture, addressFuture).join();
return buildOrderVO(
stockFuture.join(),
couponFuture.join(),
addressFuture.join()
);
// 总耗时:max(200, 150, 100) = 200ms,提速 55%!
}
改造前(串行)
0ms ├─ 查库存 200ms
200ms ├─ 查优惠券 150ms
350ms ├─ 查地址 100ms
450ms └─ 返回结果
总耗时: 450ms
改造后(并行)
0ms ├─ 查库存 200ms ┐
0ms ├─ 查优惠券 150ms ├ 同时跑!
0ms ├─ 查地址 100ms ┘
200ms └─ 返回结果
总耗时: 200ms(↓55%)
📊 场景2:报表导出 — 同步→异步+轮询
// ❌ 改造前:同步导出,大数据量直接超时
@GetMapping("/report/export")
public void exportReport(HttpServletResponse response) {
List<ReportData> data = reportService.queryAll(); // 查10万条,5秒
byte[] excel = excelService.generate(data); // 生成Excel,3秒
// 总耗时8秒,HTTP请求很可能超时!
response.getOutputStream().write(excel);
}
// ✅ 改造后:异步生成+轮询下载
// 第1步:提交导出任务,立即返回任务ID
@PostMapping("/report/submit")
public TaskVO submitExport() {
String taskId = UUID.randomUUID().toString();
// 异步生成报表
CompletableFuture.runAsync(() -> {
List<ReportData> data = reportService.queryAll();
byte[] excel = excelService.generate(data);
cacheService.put(taskId, excel, 30, TimeUnit.MINUTES); // 缓存30分钟
taskService.updateStatus(taskId, "DONE");
}, reportPool).exceptionally(ex -> {
taskService.updateStatus(taskId, "FAILED");
log.error("报表生成失败", ex);
return null;
});
return new TaskVO(taskId, "PROCESSING");
}
// 第2步:前端轮询任务状态
@GetMapping("/report/status/{taskId}")
public TaskVO getTaskStatus(String taskId) {
String status = taskService.getStatus(taskId);
return new TaskVO(taskId, status);
}
// 第3步:下载
@GetMapping("/report/download/{taskId}")
public void download(String taskId, HttpServletResponse response) {
byte[] excel = cacheService.get(taskId);
response.getOutputStream().write(excel);
}
📨 场景3:消息发送 — 同步→异步批量
// ❌ 改造前:同步逐条发邮件
public void sendNotification(List<String> users) {
for (String user : users) { // 1000个用户
emailService.send(user, content); // 每封200ms
}
// 总耗时:1000 × 200ms = 200秒!完全不可接受
}
// ✅ 改造后:CompletableFuture 并行 + 批次控制
public void sendNotification(List<String> users) {
// 分批:每批50个用户
List<List<String>> batches = Lists.partition(users, 50);
for (List<String> batch : batches) {
List<CompletableFuture<Void>> futures = batch.stream()
.map(user -> CompletableFuture.runAsync(
() -> emailService.send(user, content), emailPool
))
.collect(Collectors.toList());
// 等待当前批次全部完成,再处理下一批
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.join();
}
// 总耗时:20批 × 200ms = 4秒(从200秒→4秒,提速50倍!)
}
⏰ 场景4:定时任务优化 — 串行→并行
// ❌ 改造前:5个统计任务串行执行
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨2点
public void dailyStats() {
statsService.calcOrderStats(); // 30秒
statsService.calcUserStats(); // 20秒
statsService.calcProductStats(); // 15秒
statsService.calcRevenueStats(); // 25秒
statsService.calcTrafficStats(); // 10秒
// 总耗时:100秒
}
// ✅ 改造后:5个任务并行执行
@Scheduled(cron = "0 0 2 * * ?")
public void dailyStats() {
CompletableFuture<?>[] tasks = new CompletableFuture[]{
CompletableFuture.runAsync(() -> statsService.calcOrderStats(), statsPool),
CompletableFuture.runAsync(() -> statsService.calcUserStats(), statsPool),
CompletableFuture.runAsync(() -> statsService.calcProductStats(), statsPool),
CompletableFuture.runAsync(() -> statsService.calcRevenueStats(), statsPool),
CompletableFuture.runAsync(() -> statsService.calcTrafficStats(), statsPool),
};
CompletableFuture.allOf(tasks).join();
// 总耗时:max(30,20,15,25,10) = 30秒(从100秒→30秒,提速70%)
// 别忘了异常处理!
for (CompletableFuture<?> task : tasks) {
try { task.join(); }
catch (CompletionException e) {
log.error("统计任务失败", e.getCause());
}
}
}
4个生产场景改造总结:
① 下单接口:串行3个查询→并行 → 450ms→200ms
② 报表导出:同步→异步+轮询 → 不再超时
③ 消息发送:逐条串行→分批并行 → 200秒→4秒
④ 定时任务:5个统计串行→并行 → 100秒→30秒
通用模式:找出互不依赖的步骤 → CompletableFuture.allOf 并行执行 → join 汇总
🔥 全文总结 — 异步编程核心要点:
怎么写:
① 简单异步(不用返回值)→ @Async
② 链式编排(有依赖关系)→ CompletableFuture
③ 并行查询(互不依赖)→ CompletableFuture.allOf
④ 永远传自定义线程池,不要用默认的 commonPool
多线程注意什么:
⑤ 竞态条件:共享变量 + 写 → 用 Atomic/锁
⑥ 死锁:固定加锁顺序 + tryLock 超时
⑦ 可见性/有序性:状态标志用 volatile
异步任务注意什么:
⑧ 异常被吞:永远加 exceptionally / handle / whenComplete
⑨ @Async 的5大坑:同类调用/异常吞/无界池/事务失效/返回值
⑩ 上下文丢失:ThreadLocal(Security/MDC)不会自动传到异步线程