背景
最近看代码时看到小伙伴提交了这样一段代码
public class AsyncExecutorConfig extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
return new ThreadPoolExecutor(
10,
300,
30,
TimeUnit.SECONDS,
new LinkedBlockingDeque<>(2000),
ThreadFactoryBuilder.create().setNamePrefix("bid-async-").build(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
代码的目的是重新实现@Aysnc
的默认线程处理,于是有一个疑问,为什么要进行重写,使用默认实现不可以吗?@Async
默认的实现方式是什么?原理又是什么呢?
带着这个疑问进行了相关探索
探索
从修改默认实现上看,默认实现一定也是一个线程池,于是查看他的默认线程池是什么?首先我看到的默认实现是会使用SimpleAsyncTaskExecutor
线程池,那看SimpleAsyncTaskExecutor
线程池的实现方式,他的方式是有一个任务就去创建一个线程,而且创建的线程不会复用且不会销毁,当任务过多时,会出现cpu过高的情况,基于此,原有的实现是一定存在问题的。
随着探索的加深,发现存在另一个答案,就是他的实现也是ThreadPoolExecutor
,在springboot2.1.0之前默认是实现是SimpleAsyncTaskExecutor
,2.1.0之后默认实现变更为ThreadPoolExecutor
,我们看看变更之后是怎么实现默认线程池的
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
/**
* Bean name of the application {@link TaskExecutor}.
*/
public static final String APPLICATION_TASK_EXECUTOR_BEAN_NAME = "applicationTaskExecutor";
private final TaskExecutionProperties properties;
private final ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers;
private final ObjectProvider<TaskDecorator> taskDecorator;
public TaskExecutionAutoConfiguration(TaskExecutionProperties properties,
ObjectProvider<TaskExecutorCustomizer> taskExecutorCustomizers,
ObjectProvider<TaskDecorator> taskDecorator) {
this.properties = properties;
this.taskExecutorCustomizers = taskExecutorCustomizers;
this.taskDecorator = taskDecorator;
}
protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) {
BlockingQueue<Runnable> queue = this.createQueue(this.queueCapacity);
ThreadPoolExecutor executor;
if (this.taskDecorator != null) {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) {
public void execute(Runnable command) {
Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command);
if (decorated != command) {
ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command);
}
super.execute(decorated);
}
};
} else {
executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler);
}
默认线程池的定义了核心线程数,最大线程数,队列以及线程存活时间,他们的参数分别为
- 核心线程数:8
- 最大线程数:int的最大值
- 队列:阻塞队列且最大值为int的最大值
- 线程存活时间:60s
- 拒绝策略:AbortPolicy
对比上一个默认实现,一个很大的进步是线程是可以重复利用的,这样就大大减少了资源的使用,但是还是存在一个较大的风险,就是队列长度过长,当任务过多时会将大量待执行的任务放到队列里面,导致程序处理不过来,最大线程数其实没有利用起来,所以保险起见,还是要自己手动实现一个线程池,防止出现相关问题
结语
线程池可以很好的提高功能的效率,但是也要考虑使用过程中出现的问题,比如多线程写,又比如使用不当造成的系统性能问题,基于此,是很有必要了解他们的执行方式和原理