@Component
@Slf4j
public class SmsComponent {
@Async
public void send() {
System.out.println("方法逻辑");
}
}
二、失效情况
1、注解
@Async
的⽅法不是
public
⽅法
2、注解
@Async
的返回值只能为
void
或者
Future
3、注解
@Async
⽅法使⽤
static
修饰也会失效
4、spring
⽆法扫描到异步类,没加注解
@Async
或 @EnableAsync注解
5、调⽤⽅与被调⽅不能在同⼀个类
Spring
在扫描
bean
的时候会扫描⽅法上是否包含
@Async 注解,动态地⽣成⼀个⼦类(即proxy
代理类),当这个有注解的⽅法被调⽤的时候,实际上是由代理类来调⽤的,代理类在调⽤时增加异步作⽤如果这个有注解的⽅法是被同⼀个类中的其他⽅法调⽤的,那么该⽅法的调⽤并没有通过代理类,⽽是直接通过原来的那个 bean
,所以就失效了所以调⽤⽅与被调⽅不能在同⼀个类,主要是使⽤了动态代理,同⼀个类的时候直接调⽤,不是通过⽣成的动态代理类调⽤⼀般将要异步执⾏的⽅法单独抽取成⼀个类类中需要使⽤
@Autowired
或
@Resource
等注解⾃动注⼊,不能⾃⼰⼿动new
对象
6、在
Async
⽅法上标注
@Transactional
是没⽤的,但在
Async ⽅法调⽤的⽅法上标注@Transactional
是有效的
三、注意事项
@Async
注解没指定线程池的话,即未设置TaskExecutor时默认使⽤Spring
创建ThreadPoolTaskExecutor,
默认
8
个核⼼线程数占⽤满了之后
,
新的调⽤就会进⼊队列
,
最
⼤值是Integer.MAX_VALUE,极容易出现
OOM
,或者消息丢失。
附加:
ThreadPoolTaskExecutor
和
ThreadPoolExecutor区别?
1、ThreadPoolExecutor
,这个类是
JDK
中的线程池类,继承⾃ Executor,⾥⾯有⼀个
execute()
⽅法,⽤来执⾏线程,线程 池主要提供⼀个线程队列,队列中保存着所有等 待状态的线 程,避免了创建与销毁的额外开销。
2、ThreadPoolTaskExecutor
,是
spring
包下的,是
Spring
为我 们提供的线程池类 Spring 异步线程池的接⼝类是
TaskExecutor
,本质还是 java.util.concurrent.Executor
源代码位置:
TaskExecutionProperties
public static class Pool {
private int queueCapacity = Integer.MAX_VALUE;
private int coreSize = 8;
private int maxSize = Integer.MAX_VALUE;
private boolean allowCoreThreadTimeout = true;
TaskExecutionAutoConfiguration
四、解决方案 spring 会先搜索 TaskExecutor 类型的 bean 或者名字为 taskExecutor的 Executor 类型的 bean, 所以我们最好来⾃定义⼀个线程池,加⼊ Spring IOC 容器⾥⾯,即可覆盖@Configuration
@EnableAsync
public class ThreadPoolTaskConfig {
@Bean("threadPoolTaskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
//线程池创建的核⼼线程数,线程池维护线程的最少数ᰁ,即使没有任务需要执⾏,也会⼀直存活
//如果设置allowCoreThreadTimeout=true(默认false)时,核⼼线程会超时关闭
threadPoolTaskExecutor.setCorePoolSize(4);
//最⼤线程池数ᰁ,当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
//当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务⽽抛出异常
threadPoolTaskExecutor.setMaxPoolSize(8);
//缓存队列(阻塞队列)当核⼼线程数达到最⼤时,新任务会放在队列中排队等待执⾏
threadPoolTaskExecutor.setQueueCapacity(124);
//当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数ᰁ=corePoolSize
//允许线程空闲时间60秒,当maxPoolSize的线程在空闲时间到达的时候销毁
//如果allowCoreThreadTimeout=true,则会直到线程数ᰁ=0
threadPoolTaskExecutor.setKeepAliveSeconds(30);
//spring 提供的 ThreadPoolTaskExecutor 线程池,是有setThreadNamePrefix() ⽅法的。
//jdk 提供的ThreadPoolExecutor 线程池是没有setThreadNamePrefix() ⽅法的
threadPoolTaskExecutor.setThreadNamePrefix("xcw-Async前缀:");
threadPoolTaskExecutor.setWaitForTasksToCompleteonShutdown(true);
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CallerRunsPolicy():交由调⽤⽅线程运⾏,⽐如main 线程;如果添加到线程池失败,那么主线程会⾃⼰去执⾏该任务,不会等待线程池中的线程去执⾏
//AbortPolicy():该策略是线程池的默认策略,如果线程池队列满了丢掉这个任务并且抛出RejectedExecutionException异常。
//DiscardPolicy():如果线程池队列满了,会直接丢掉这个任务并且不会有任何异常
//DiscardOldestPolicy():丢弃队列中最⽼的任务,队列满了,会将最早进⼊队列的任务删掉腾出空间,再尝试加⼊队列
threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
threadPoolTaskExecutor.initialize();
return threadPoolTaskExecutor;
}
}
//
使⽤实战, 启动类可以不加
@EnableAsync
,改上⾯加
@Async("threadPoolTaskExecutor")



