异步
场景
发送邮件、生成报表、调用第三方接口等操作耗时且调用方不关心结果,同步执行会拖慢主流程。@Async 让方法在独立线程执行,调用方立即返回。
开启与基本用法
在配置类上加 @EnableAsync,再给方法标 @Async:
@Service
public class NotificationService {
private static final Logger log = LoggerFactory.getLogger(NotificationService.class);
@Async
public void sendWelcomeMail(String to) {
// 实际在异步线程执行,调用方不会阻塞在这里
log.info("异步发送邮件给 {}", to);
}
}
调用方照常调用即可,方法立即返回。需要返回结果时用 CompletableFuture:
@Async
public CompletableFuture<String> buildReport() {
String url = heavyWork(); // 耗时计算
return CompletableFuture.completedFuture(url);
}
// 调用方:reportFuture.thenAccept(...) 或阻塞 get()
@Async 与 17 章事务、20 章定时任务共享同一个坑:同类内部自调用不走代理,this.sendWelcomeMail(...) 会变成同步执行。异步逻辑要放到另一个受管 Bean 里,或注入自身代理。
线程池配置
让 @Async 有明确的执行器:Boot 会自动提供一个默认 ThreadPoolTaskExecutor(名为 applicationTaskExecutor),其核心/最大线程数、队列容量等参数由 spring.task.execution.* 配置项控制(属性与默认值的具体清单以官方文档为准)。更常见的是自定义并显式指定:
@Bean("mailExecutor")
public Executor mailExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4); // 常驻线程
executor.setMaxPoolSize(8); // 最大线程
executor.setQueueCapacity(200); // 等待队列容量
executor.setThreadNamePrefix("mail-");
executor.initialize();
return executor;
}
@Async("mailExecutor") // 指定使用上面的线程池
public void sendWelcomeMail(String to) { ... }
生产建议明确线程池参数并监控队列积压,避免任务堆积拖垮内存。
异常处理
- 返回
CompletableFuture的异步方法:异常封装在 Future 里,调用方通过get/whenComplete感知; - void 异步方法:异常默认只打到异步线程,调用方无从感知。应在方法内自行 try-catch 并记录日志,或实现
AsyncUncaughtExceptionHandler统一处理(相关 API 的推荐写法以官方文档为准)。
线程上下文注意
跨线程时 ThreadLocal 不会自动传递:MDC 日志编号、Spring Security 用户上下文等需要显式传递(例如给线程池配 TaskDecorator 复制上下文),否则异步线程里取不到登录用户等信息。
小结
@EnableAsync + @Async 解决了「快速返回、后台执行」的需求。正确用法四要素:外部 Bean 调用(代理生效)、显式线程池、异常自处理、跨线程上下文按需传递。