引言:
在Spring项目中实现异步的方式:
- @Async
- 消息队列
- CompletableFuture
- WebAsyncTask/DeferredResult
- @Scheduled+@Async
最常用的就是@Async,这篇文章帮助你快速上手@Async以及正确使用这个注解;
1.简介:
@Async是Spring的注解,可以加在类或方法上。通俗的来讲,如果加上了这个注解,那么该类或者该方法在使用时将会进行异步处理,也就是创建一个线程来实现这个类或者方法,实现多线程。
2.使用:
1.需要在@SpringBootApplication启动类或者@configure注解类上 添加注解@EnableAsync启动多线程注解。
2.在需要异步执行的方法上添加@Async注解。
3.工作原理
1.Spring 启动时创建代理对象
- Spring 扫描到
AsyncService类中有@Async方法 - 由于开启了
@EnableAsync,Spring 会为这个 Bean 创建一个 JDK 动态代理(或 CGLib 代理) - 最终注入到
TestController中的asyncService不是原始对象,而是代理对象
2.调用 doSomething() 时,代理拦截
当你写:
实际执行的是 代理类的 doSomething() 方法,而不是你写的原始方法。
代理内部逻辑类似:
less
doSomething();
if(方法有 @Async)
// 1. 获取配置的TaskExecutor
Executor executor = getAsyncExecutor();
// 2. 把原方法包装成 Runnable
Runnable task = () -> originalObject.doSomething();
// 3. 提交到线程池 → 异步执行
executor.execute(task);
// 4.立即返回(如果是 void 方法)
return
3:线程池中的工作线程执行任务
- 线程池(如
ThreadPoolTaskExecutor)从队列取出任务 - 在某个工作线程(比如
Async-1)中,真正执行你写的doSomething()方法体 - 这个过程与主线程完全无关
4.线程池的选择和配置
1.Spring 会使用一个内置的默认线程池策略。这个"默认"其实不是真正的线程池,而是一个特殊实现:SimpleAsyncTaskExecutor


- 每次调用 @Async 方法时,都会 创建一个全新的线程 来执行任务。
- 执行完后线程直接销毁,不复用、不缓存、无队列。
风险:
- 内存溢出(OOM)
- CPU 上下文切换开销剧增 → 系统卡死
- 无法控制并发度
2.自己配置线程池
1.配置

2.指定

5.注意事项:@Async失效的常见情况
- 未启用 @EnableAsync
- 方法不是 public
- 同类中的内部调
- 代理对象未生效(未经benan工厂管理,如new对象)
- 配置了错误的线程池或未正确初始化
- 异步方法定义在非 Spring 管理的类中
- 使用了CGLIB代理但方法是final(不可被代理)
- 与@Transactional冲突(都是代理对象实现的,可能冲突)
写在最后:想写的尽可能简洁,没想到还是有这么多,如果你能耐心看完,我觉得一定能帮助到你。