寫在前面的話
該文是在讀到的眾多有關(guān)@Async自定義線程池的博文中分析最為詳細(xì)準(zhǔn)確的。
本文為轉(zhuǎn)載文章,版權(quán)所有歸文章原始作者,如有需要請從原文轉(zhuǎn)載:
原文鏈接:https://www.kuangstudy.com/bbs/1407940516238090242
在 秋招沖刺班 的視頻中,飛哥講到 [@Async](https://github.com/Async "@Async") 注解默認(rèn)情況下用的是SimpleAsyncTaskExecutor 線程池,并且提到日志中的 taskId 是一直增長的。抱著探索的精神展開了如下測試:
問題復(fù)現(xiàn)
- SpringBoot 版本 2.4.7
- 測試機(jī)配置:雙核Intel Core i5
異步方法如下:
@Async
public void sendMsg() {
// todo :模擬耗時(shí)5秒
try {
Thread.sleep(5000);
log.info("---------------發(fā)送消息--------");
} catch (Exception ex) {
ex.printStackTrace();
}
}
// 添加積分,用異步進(jìn)行處理和標(biāo)記
@Async
public void addScore() {
// todo :模擬耗時(shí)5秒
try {
Thread.sleep(5000);
log.info("---------------處理積分--------");
} catch (Exception ex) {
ex.printStackTrace();
}
}
啟動項(xiàng)目,多次調(diào)用這兩個(gè)方法,得到如下日志結(jié)果??梢园l(fā)現(xiàn)在當(dāng)前環(huán)境下 task-${id} 這個(gè) id 并不是一直增長的,而是一直在復(fù)用 1-8。這個(gè)時(shí)候可能就會有的小伙伴們會比較好奇,默認(rèn)的不是 SimpleAsyncTaskExecutor 嗎?為什么從日志打印的效果上看像是一直在復(fù)用 8 個(gè)線程,難道用的是 ThreadPoolTaskExecutor?

驗(yàn)證猜想
為了驗(yàn)證我們的猜想,決定到源碼里面去看一看,到底是用的是什么 TaskExecutor。
首先找到了 @Async 的攔截器類org.springframework.aop.interceptor.AsyncExecutionInterceptor
// 當(dāng)執(zhí)行 @Async 修飾的異步方法時(shí),就會進(jìn)入到這個(gè)方法中
@Override
@Nullable
public Object invoke(final MethodInvocation invocation) throws Throwable {
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
final Method userDeclaredMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
// 在這里得到了方法的執(zhí)行器
AsyncTaskExecutor executor = determineAsyncExecutor(userDeclaredMethod);
if (executor == null) {
throw new IllegalStateException(
"No executor specified and no default executor set on AsyncExecutionInterceptor either");
}
// ...
return doSubmit(task, executor, invocation.getMethod().getReturnType());
}
// AsyncExecutionInterceptor 父類中的方法,可以得到異步方法對應(yīng)的執(zhí)行器
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
// executors 是一個(gè)緩存,只要執(zhí)行過一次就會記錄它要使用的 TaskExecutor,不需要每次執(zhí)行都去尋找要使用的 `TaskExecutor`
AsyncTaskExecutor executor = this.executors.get(method);
if (executor == null) {
Executor targetExecutor;
// 如果通過 @Async("myExectuor") 指定了執(zhí)行器 "myExectuor",就找指定的,如果沒有就用默認(rèn)的。
String qualifier = getExecutorQualifier(method);
if (StringUtils.hasLength(qualifier)) {
targetExecutor = findQualifiedExecutor(this.beanFactory, qualifier);
} else {
// 通過觀察源碼,可以發(fā)現(xiàn) defaultExecutor 是通過 getDefaultExecutor 得到的
targetExecutor = this.defaultExecutor.get();
}
if (targetExecutor == null) {
return null;
}
executor = (targetExecutor instanceof AsyncListenableTaskExecutor ?
(AsyncListenableTaskExecutor) targetExecutor : new TaskExecutorAdapter(targetExecutor));
this.executors.put(method, executor);
}
return executor;
}
// AsyncExecutionInterceptor 類中的 getDefaultExecutor
// !!!!!!!!!!!!!重點(diǎn)!!!!!!!!!!!!!
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
// 調(diào)用父類 super.getDefaultExecutor 得到 Executor
Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
// 如果沒有得到默認(rèn)的 Executor,則選用 SimpleAsyncTaskExecutor
return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}
// 父類中的 getDefaultExecutor
// 會去 Spring 的容器中找有沒有 TaskExecutor 或名稱為 'taskExecutor' 為 Executor 的
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
if (beanFactory != null) {
try {
return beanFactory.getBean(TaskExecutor.class);
} catch (NoUniqueBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
} catch (NoSuchBeanDefinitionException ex) {
return beanFactory.getBean("taskExecutor", Executor.class);
}
}
return null;
}
既然知道了在哪里記錄了異步方法使用的什么 Executor,我們可以通過 DEBUG 模式去查看。
但是我們通過 DEBUG 查看 executors 緩存卻發(fā)現(xiàn)在當(dāng)前版本中的 SpringBoot @Async 默認(rèn)是用的是居然是 ThreadPoolTaskExecutor。

小結(jié):在這里我簡單總結(jié)一下,在 SpringFramework 中,如果沒有自定義的 TaskExecutor 或名稱為 ‘taskExecutor’ 的 Bean 對象,那么就會使用 SimpleAsyncTaskExecutor 。所以在當(dāng)前版本 SpringFramework 中 @Async 默認(rèn)是用的是 SimpleAsyncTaskExecutor。
但是,從 DEBUG 模式中我們能發(fā)現(xiàn),Executor defaultExecutor = super.getDefaultExecutor(beanFactory); 這里居然取到了 Executor,并且還是 ThreadPoolTaskExecutor,那么我們就可以想到是不是有誰幫我們創(chuàng)建了一個(gè) ThreadPoolTaskExecutor 的 Bean 對象呢?
為什么容器中會有 ThreadPoolTaskExecutor 的 Bean 對象
通過上面的實(shí)現(xiàn)結(jié)果和問題,我聯(lián)想到了 spring-boot-autoconfigure 這個(gè)包,因?yàn)檫@個(gè)包會自動幫我們生成一些需要的 Bean 對象。最終成功在 org.springframework.boot.autoconfigure.task 下找到了如下類:
@ConditionalOnClass(ThreadPoolTaskExecutor.class)
@Configuration
@EnableConfigurationProperties(TaskExecutionProperties.class)
public class TaskExecutionAutoConfiguration {
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;
}
@Bean
@ConditionalOnMissingBean
public TaskExecutorBuilder taskExecutorBuilder() {
TaskExecutionProperties.Pool pool = this.properties.getPool();
TaskExecutorBuilder builder = new TaskExecutorBuilder();
builder = builder.queueCapacity(pool.getQueueCapacity());
builder = builder.corePoolSize(pool.getCoreSize());
builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive());
builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
builder = builder.customizers(this.taskExecutorCustomizers);
builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
return builder;
}
// 只要沒有 Executor 的 Bean 對象,那么就會幫你生成一個(gè) ThreadPoolTaskExecutor 的 Bean 對象
@Lazy
@Bean(name = APPLICATION_TASK_EXECUTOR_BEAN_NAME)
@ConditionalOnMissingBean(Executor.class)
public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
return builder.build();
}
}
小結(jié):由于 spring-boot-autoconfigure 是 SpringBoot 一個(gè)重要的依賴,所以只要是 SpringBoot 項(xiàng)目就一定會依賴它,可以斷定 ThreadPoolTaskExecutor 是 SpringBoot 項(xiàng)目中 Executor 的默認(rèn) Bean 對象。而 [@Async](https://github.com/Async "@Async") 在選擇執(zhí)行器的時(shí)候會先去 IOC 容器中先找是否有 TaskExecutor 的 Bean對象,所以在當(dāng)前版本 SpringBoot 中,@Async 的默認(rèn) TaskExecutor 是 ThreadPoolTaskExecutor。
驗(yàn)證飛哥的說法
既然飛哥說默認(rèn)的 SimpleAsyncTaskExecutor,我選擇相信飛哥,因?yàn)榭赡芤郧暗陌姹镜?SpringBoot 并不會默認(rèn)幫我們創(chuàng)建一個(gè) ThreadPoolTaskExecutor 的 Bean 對象。
查看源碼,我找到了一個(gè)關(guān)鍵的信息。這說明 TaskExecutionAutoConfiguration 是從 2.1.0 版本才開始有的。

那么我將版本換到 2.1.0 之前再試試。在這里我選用了 2.1.0 的上一個(gè)版本 2.0.9 (完整的應(yīng)該是 2.0.9.RELEASE)。
通過查看 spring-boot-autoconfigure-2.0.9 包,并沒有找到 TaskExecutionAutoConfiguration 這個(gè)類。但是還需要通過代碼的執(zhí)行效果來驗(yàn)證。
驗(yàn)證 task-id 是不是一直增加

驗(yàn)證是由于從 Spring 容器中拿不到,然后默認(rèn)使用 SimpleAsyncTaskExecutor

注:驗(yàn)證時(shí),一定要是第一次調(diào)用異步方法,要不然就會走緩存。
驗(yàn)證緩存中存是的 SimpleAsyncTaskExecutor

完善飛哥的說法
總結(jié):在 SpringBoot 2.0.9 版本及以前,@Async 默認(rèn)使用的是 SimpleAsyncTaskExecutor;從 2.1.0 開始到當(dāng)前最新的 2.5.1,@Async 默認(rèn)使用的是 ThreadPoolTaskExecutor。
版權(quán)聲明
版權(quán)聲明:本文為博主原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接和本聲明,KuangStudy,以學(xué)為伴,一生相伴!
原文鏈接:https://www.kuangstudy.com/bbs/1407940516238090242