Spring 异步处理(@Async)是真正决定你的系统能不能“扛住并发”的进阶特性之一。
99% 的初学者只会写个 @Async 就以为完事了,结果生产一并发就崩。
下面给你 2025 年最新、最地道的 Spring Boot 3.x(JDK 17+)异步全套最佳实践,直接可用于银行、电商、互联网大厂核心系统。
1. 核心对比总览(2025 最新版)
| 方案 | 是否真正异步 | 是否支持返回值 | 是否支持异常捕获 | 是否支持线程池隔离 | 推荐场景 |
|---|---|---|---|---|---|
直接 @Async(不配置) | 同步(假异步) | 不支持 | 抛不出来 | 无 | 永远不要用! |
@Async + 默认线程池 | 真异步 | 不支持 | 吃掉异常 | 无 | 小项目、日志打印 |
自定义线程池 + @Async | 真异步 | 支持(Future/CompletableFuture) | 可捕获 | 支持 | 90% 生产项目 |
ThreadPoolTaskExecutor + 队列监控 | 真异步 | 支持 | 支持 | 支持 | 大中型项目 |
| 异步 + 分布式调用追踪(MDC + Async) | 真异步 | 支持 | 支持 | 支持 | 微服务项目 |
| Spring Boot 3 + Virtual Threads(JDK 21) | 百万级并发 | 支持 | 支持 | 内置 | 2025 年新趋势(超高并发系统 |
2. 正确打开方式(99% 的人第一步就错了)
@EnableAsync // 主类一定要加!!
@Configuration
public class AsyncConfig {
// 强烈推荐:每个业务一个线程池,彻底隔离
@Bean("orderAsyncExecutor")
public ThreadPoolTaskExecutor orderExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(8);
executor.setMaxPoolSize(32);
executor.setQueueCapacity(1000); // 关键:队列满了要拒绝!
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("order-async-");
// 生产必备:拒绝策略用 CallerRunsPolicy(打满时让调用线程自己执行,防止雪崩)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 可选:优雅关闭
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
@Bean("mailAsyncExecutor")
public ThreadPoolTaskExecutor mailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("mail-async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
3. 使用方式大全
@Service
@RequiredArgsConstructor
public class OrderService {
// 1. 无返回值(最常用:发邮件、记录日志、埋点)
@Async("mailAsyncExecutor") // 指定线程池!!
public void sendOrderConfirmEmail(Long orderId) {
try {
// 模拟耗时
Thread.sleep(3000);
log.info("邮件已发送,订单号:{}", orderId);
} catch (Exception e) {
log.error("发送邮件失败", e); // 异常不会抛到主线程!
}
}
// 2. 有返回值(Future)
@Async("orderAsyncExecutor")
public CompletableFuture<String> generateReport(Long orderId) {
try {
Thread.sleep(5000);
return CompletableFuture.completedFuture("报告生成成功");
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
// 3. 有返回值 + 异常传播(推荐)
@Async("orderAsyncExecutor")
public CompletableFuture<UserDTO> loadUserDetail(Long userId) {
return CompletableFuture.supplyAsync(() -> {
// 这里抛异常会自动包装到 CompletableFuture
if (userId == 666) throw new BusinessException("用户不存在");
return new UserDTO(userId, "张三");
}, taskExecutor); // 可指定线程池
}
}
Controller 调用方式:
@GetMapping("/order/create")
public R<Void> createOrder() {
Long orderId = 12345L;
// 火力全开异步(完全不阻塞主线程)
orderService.sendOrderConfirmEmail(orderId);
orderService.sendSms(orderId);
orderService.recordLog(orderId);
orderService.pushToDataWarehouse(orderId);
return R.ok("订单创建成功,主流程已完成,通知类任务异步处理中");
}
// 需要返回值时
@GetMapping("/report")
public CompletableFuture<R<String>> getReport() {
return orderService.generateReport(123L)
.thenApply(R::ok)
.exceptionally(e -> R.error(e.getMessage()));
}
4. 生产级进阶技巧(大厂标配)
1. 异步异常统一捕获(否则异常被吞掉!)
@Component
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.error("异步任务异常:方法={}, 参数={}, 异常={}",
method.getName(), Arrays.toString(params), ex.getMessage(), ex);
// 可推送到告警系统
// DingTalkUtil.send("异步任务崩溃啦!", ex);
}
}
// 在配置类中注册
@Bean
public AsyncConfigurer asyncConfigurer() {
return new AsyncConfigurer() {
@Override
public Executor getAsyncExecutor() {
return orderExecutor();
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
};
}
2. 异步方法内继续使用 MDC 链路追踪(微服务必备)
@Async("orderAsyncExecutor")
public void processOrder(Long orderId) {
// 关键:手动传递 MDC
Map<String, String> contextMap = MDC.getCopyOfContextMap();
CompletableFuture.runAsync(() -> {
if (contextMap != null) {
MDC.setContextMap(contextMap);
}
try {
log.info("开始处理订单:{}", orderId);
// 业务代码...
} finally {
MDC.clear();
}
});
}
3. 2025 最新:虚拟线程(Virtual Threads)——百万级并发杀器
# application.yml (Spring Boot 3.2+ + JDK 21)
spring:
threads:
virtual:
enabled: true # 一键开启虚拟线程,所有 @Async 自动用虚拟线程!
开启后你甚至不需要配置线程池,Spring 自动把所有 @Async 换成虚拟线程,QPS 轻松 10w+。
5. 常见坑 & 终极建议
| 坑点 | 解决方案 |
|---|---|
同一个类里调用 @Async 方法不生效 | 必须通过 Spring 代理调用(注入自己或用 ApplicationContext 手动获取代理) |
| 异常被吞掉,线上查不到原因 | 实现 AsyncUncaughtExceptionHandler |
| 线程池打满导致主线程卡死 | 队列满了用 CallerRunsPolicy,让调用线程自己干活 |
异步方法返回 void 时无法感知执行结果 | 改用 CompletableFuture<Void> |
| 异步任务太多导致 OOM | 限制队列长度 + 监控 + 熔断 |
6. 推荐架构(2025 真实生产实践)
| 项目规模 | 推荐方案 |
|---|---|
| 小项目 | @EnableAsync + 默认线程池(SimpleAsyncTaskExecutor) |
| 中大型项目 | 多个自定义 ThreadPoolTaskExecutor + 异常处理器 + MDC 传递 |
| 高并发互联网项目 | 开启 spring.threads.virtual.enabled=true(JDK 21) |
| 银行/金融核心系统 | 自定义线程池 + 队列监控 + 告警 + 降级 + CallerRunsPolicy |
需要我直接给你一个完整的生产级异步模板项目吗?包含:
- 6 个业务隔离线程池
- 统一异常处理 + 告警
- MDC 自动传递
- 异步任务监控指标(Micrometer + Prometheus)
- 虚拟线程一键开关
直接说一声,我发你 GitHub 地址,拿来即用。