「@Async」SpringBoot中@Async默認(rèn)的TaskExecutor到底是哪個(gè)?

寫在前面的話

該文是在讀到的眾多有關(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?

11.jpg

驗(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

22.jpg

小結(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 版本才開始有的。

33.jpg

那么我將版本換到 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 是不是一直增加

44.jpg

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

55.jpg

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

驗(yàn)證緩存中存是的 SimpleAsyncTaskExecutor

66.jpg

完善飛哥的說法

總結(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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容