1. @Async的作用
一、核心作用
@Async 是 Spring 提供的异步执行注解,作用:
- 把被标注的方法放到独立线程异步执行,不会阻塞当前调用主线程;
- 实现任务解耦、提升接口响应速度,常用于异步通知、日志、文件导出、消息发送等耗时操作。
二、使用前提(必须两步,缺一不可)
- 启动类添加
@EnableAsync开启异步功能
java
@SpringBootApplication
@EnableAsync // 开启Spring异步支持
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
}
@Async写在public 方法上,且调用方和异步方法不在同一个类(内部调用不生效,AOP 无法拦截)
三、两种返回形式
1. 无返回值(void)
适合不需要接收结果的耗时任务:发短信、记录操作日志
java
@Async
public void sendSms(String phone) {
// 耗时网络操作,异步执行,不阻塞主线程
}
2. 有返回值(返回 Future<T> / CompletableFuture<T>)
需要获取异步执行结果、做结果聚合
java
@Async
public Future<Integer> countOrder() {
int total = orderMapper.selectCount(null);
return CompletableFuture.completedFuture(total);
}
// 调用
Future<Integer> future = orderService.countOrder();
Integer total = future.get(); // 阻塞等待异步结果
四、底层原理
Spring 通过 AOP 动态代理 拦截 @Async 方法:
- 拦截到方法后,把任务交给内置线程池
SimpleAsyncTaskExecutor; - 新开子线程执行目标方法,主线程直接返回,不等待方法执行完毕。
五、默认线程池问题(重要)
默认使用 SimpleAsyncTaskExecutor:每次调用新建一个线程,无复用、无限创建线程,高并发会 OOM。
生产环境自定义线程池方案
java
@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程
executor.setMaxPoolSize(50); // 最大线程
executor.setQueueCapacity(200); // 队列
executor.setThreadNamePrefix("async-task-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
// 指定使用自定义线程池
@Async("taskExecutor")
public void asyncTask(){}
六、常见坑
- 同类内部调用失效
java
// 同一个类里调用,AOP代理拦截不到,同步执行
public void test(){
asyncMethod(); // 无效,同步
}
@Async
public void asyncMethod(){}
解决:把异步方法抽离到单独 Service,或者通过 ApplicationContext 获取代理对象调用。
-
事务失效异步方法会新开线程,子线程和主线程是独立事务,主线程回滚不影响异步方法。
-
异常捕获void 异步方法抛出异常主线程感知不到,需要自定义
AsyncUncaughtExceptionHandler统一捕获异常告警。 -
事务传播问题主线程事务不会传递到异步子线程,各自独立。
七、适用场景 & 不适用场景
适用
- 异步通知:短信、邮件、站内信
- 后置统计:操作日志、埋点数据上报
- 耗时计算:报表生成、大数据导出
- MQ 消息异步发送
不适用
- 需要同步事务保证一致性的核心业务(订单创建 + 库存扣减不能分开异步)
- 需要实时拿到返回结果且不能阻塞的简单接口(同步即可)
八、和手动创建线程的区别
@Async托管线程池,统一管控、可配置拒绝策略;- 统一异常处理、线程复用,不用手动管理 Thread;
- 支持返回 Future 异步结果获取,代码更简洁。