正确使用@Async,避免踩坑_@async注意事项
用法和介绍
功能描述
@Async是Spring提供的使方法异步的注解
使用方法
- SpringBoot项目启动类增加@EnableAsync注解,开启异步功能
- 给需要异步的方法增加@Async("")注解,一定要指定线程池名称
代码示例
@EnableAsync
@SpringBootApplication
public class StartApplication {
public static void main(String[] args) {
SpringApplication.run(StartApplication.class, args);
}
}
@Service
public class AsyncService {
/**
* 使用默认线程池
*/
@Async
public void say(){ }
/**
* 指定线程池名称
*/
@Async("getAsyncExecutor")
public void eat(){ }
}
@Configuration
public class MyAsyncConfiguration {
//核心线程数
private static final int CORE_POOL_SIZE = 5;
//最大线程数
private static final int MAX_POOL_SIZE = 15;
//队列大小
private static final int QUEUE_CAPACITY = 100;
//线程池中的线程的名称前缀
private static final String THREAD_NAME = "MyThread-";
@Bean
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//配置核心线程数
executor.setCorePoolSize(CORE_POOL_SIZE);
//配置最大线程数
executor.setMaxPoolSize(MAX_POOL_SIZE);
//配置队列大小
executor.setQueueCapacity(QUEUE_CAPACITY);
//配置线程池中的线程的名称前缀
executor.setThreadNamePrefix(THREAD_NAME);
//配置线程池拒绝策略,设置为CallerRunsPolicy,当线程和队列都满了,由发起线程的主线程自己执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
已实现的TaskExecutor线程池
- SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
- SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作,只适用于不需要多线程的地方。
- ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类 。
- SimpleThreadPoolTaskExecutor:是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。
- ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对java.util.concurrent.ThreadPoolExecutor的包装。
为什么说一定要指定线程池名称
@Async依赖线程池,当我们未指定线程池@Async会如何选择呢?
1. 当系统未配置任何线程池时
SpringBoot会默认添加一个coreSize=8的 ThreadPoolTaskExecutor 无界线程池,名称applicationTaskExecutor。
2. 当只配置了一个TaskExecutor线程池时
使用此线程池
3. 当只配置了一个线程池,但不是TaskExecutor线程池时
使用 SimpleAsyncTaskExecutor 线和池
4. 当有多个TaskExecutor线程池时
默认使用 SimpleAsyncTaskExecutor 线程池,如果指定就使用指定的线程池(即使不是TaskExecutor类型的线程池也可以)
注意:SimpleAsyncTaskExecutor 不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。强烈建议开发时指定线程池的名称,避免创建多个TaskExecutor实例后,使用 SimpleAsyncTaskExecutor 线和池的情况发生