启动类注解@EnableAsync
@EnableAsync
@SpringBootApplication
public class ThreadSampleApplication {
public static void main(String[] args) {
SpringApplication.run(ThreadSampleApplication.class, args);
}
}
自定义线程池
业务量较大时,需要自定义线程池配置,否则spring会使用默认线程池,最大线程数没有限制,如果线程没有正确结束,最终会导致内存溢出.
线程池的流程:
- 当线程数小于核心线程数时,创建线程.
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列.
- 当线程数大于等于核心线程数,且任务队列已满时分两种情况:
- 若线程数小于最大线程数,创建线程.
- 若线程数等于最大线程数,执行拒绝策略.
@Configuration
@EnableAsync
public class ThreadPoolConfig {
// 当前机器的CPU核心数
private static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* 任务提交时 CorePoolSize –> QueueCapacity –> MaxPoolSize –> 拒绝策略
*/
@Bean(name = "threadPoolTaskExecutor")
public Executor asyncServiceThreadPool() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(AVAILABLE_PROCESSORS + 1);//线程池核心线程,正常情况下开启的线程数. CPU核数 + 1
executor.setQueueCapacity(AVAILABLE_PROCESSORS * 10);//线程队列容量。当核心线程数都被占用,多余的任务会存到此处
executor.setMaxPoolSize(AVAILABLE_PROCESSORS * 2);//最大线程数
executor.setKeepAliveSeconds(3600);//设置线程活跃时间(线程池中空闲线程等待工作的超时时间)
executor.setThreadNamePrefix("threadPoolTaskExecutor-");//线程名字前缀
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//设置拒绝策略
executor.setAllowCoreThreadTimeOut(true);//设置核心线程池超时关闭,默认false一直存在
executor.setKeepAliveSeconds(300);//设置普通线程超时关闭时间
executor.initialize();
return executor;
}
}
在异步执行的方法上加注解@Async,name指定使用自定义线程池配置
/**
* 使用自定义线程池
*/
@Async("threadPoolTaskExecutor")
public void doAsyncJob() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
拒绝策略 setRejectedExecutionHandler()
它是RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列(QueueCapacity)满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。
线程池提供了4种拒绝策略:
- AbortPolicy:直接抛出异常,这是默认策略.
- CallerRunsPolicy:用调用者所在的线程来执行任务.
- DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务.
- DiscardPolicy:直接丢弃任务.
注意:@Async失效情况:
-
注解@Async的方法不是public方法
-
注解@Async的返回值只能为void或者Future
-
注解@Async方法使用static修饰也会失效
-
没加注解@Async 或 @EnableAsync注解,导致spring无法扫描到异步类
-
调用方与被调方不能在同一个类:Spring在扫描bean的时候会扫描方法上是否包含@Async注解,动态地生成一个子类(即proxy代理类),当这个有注解的方法被调用的时候,实际上是由代理类来调用的,代理类在调用时增加异步作用
如果这个有注解的方法是被同一个类中的其他方法调用的,那么该方法的调用并没有通过代理类,而是直接通过原来的那个 bean, 所以调用方与被调方不能在同一个类,一般将要异步执行的方法单独抽取成一个类,类中需要使用@Autowired或@Resource等注解自动注入,不能自己手动new对象. -
在Async 方法上标注@Transactional是没用的,但在Async 方法调用的方法上标注@Transactional 是有效的