最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Async的線程池使用選擇解析

 更新時(shí)間:2023年06月15日 16:59:31   作者:我是一顆小虎牙_  
這篇文章主要為大家介紹了Async的線程池使用選擇解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

在Spring中我們經(jīng)常會(huì)用到異步操作,注解中使用 @EnableAsync@Async 就可以使用它了。但是最近發(fā)現(xiàn)在異步中線程號(hào)使用的是我們項(xiàng)目中自定義的線程池 ThreadPoolTaskExecutor 而不是之前熟悉的 SimpleAsyncTaskExecutor

那么來(lái)看一下他的執(zhí)行過(guò)程吧。

正文

  • 首先要使異步生效,我們得在啟動(dòng)類中加入 @EnableAsync 那么就點(diǎn)開(kāi)它看看。它會(huì)使用 @Import 注入一個(gè) AsyncConfigurationSelector 類,啟動(dòng)是通過(guò)父類可以決定它使用的是配置類 ProxyAsyncConfiguration 。
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
    private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME = "org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
    public AsyncConfigurationSelector() {
    }
    @Nullable
    public String[] selectImports(AdviceMode adviceMode) {
        switch(adviceMode) {
        case PROXY:
            return new String[]{ProxyAsyncConfiguration.class.getName()};
        case ASPECTJ:
            return new String[]{"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration"};
        default:
            return null;
        }
    }
}
  • 點(diǎn)開(kāi)能夠看到注入一個(gè) AsyncAnnotationBeanPostProcessor 。它實(shí)現(xiàn)了 BeanPostProcessor 接口,因此它是一個(gè)后處理器,用于將 Spring AOPAdvisor 應(yīng)用于給定的 bean 。從而該bean上通過(guò)異步注解所定義的方法在調(diào)用時(shí)會(huì)被真正地異步調(diào)用起來(lái)。
public class ProxyAsyncConfiguration extends AbstractAsyncConfiguration {
    public ProxyAsyncConfiguration() {
    }
    @Bean(
        name = {"org.springframework.context.annotation.internalAsyncAnnotationProcessor"}
    )
    @Role(2)
    public AsyncAnnotationBeanPostProcessor asyncAdvisor() {
        Assert.notNull(this.enableAsync, "@EnableAsync annotation metadata was not injected");
        AsyncAnnotationBeanPostProcessor bpp = new AsyncAnnotationBeanPostProcessor();
        bpp.configure(this.executor, this.exceptionHandler);
        Class<? extends Annotation> customAsyncAnnotation = this.enableAsync.getClass("annotation");
        if (customAsyncAnnotation != AnnotationUtils.getDefaultValue(EnableAsync.class, "annotation")) {
            bpp.setAsyncAnnotationType(customAsyncAnnotation);
        }
        bpp.setProxyTargetClass(this.enableAsync.getBoolean("proxyTargetClass"));
        bpp.setOrder((Integer)this.enableAsync.getNumber("order"));
        return bpp;
    }
}
  • AsyncAnnotationBeanPostProcessor 的父類實(shí)現(xiàn)了 BeanFactoryAware ,那么會(huì)在 AsyncAnnotationBeanPostProcessor 實(shí)例化之后回調(diào) setBeanFactory() 來(lái)實(shí)例化切面 AsyncAnnotationAdvisor 。
public void setBeanFactory(BeanFactory beanFactory) {
    super.setBeanFactory(beanFactory);
    //定義一個(gè)切面
    AsyncAnnotationAdvisor advisor = new AsyncAnnotationAdvisor(this.executor, this.exceptionHandler);
    if (this.asyncAnnotationType != null) {
        advisor.setAsyncAnnotationType(this.asyncAnnotationType);
    }
    advisor.setBeanFactory(beanFactory);
    this.advisor = advisor;
}
  • AsyncAnnotationAdvisor 構(gòu)造和聲明切入的目標(biāo)(切點(diǎn))和代碼增強(qiáng)(通知)。
    public AsyncAnnotationAdvisor(
            @Nullable Supplier<Executor> executor, @Nullable Supplier<AsyncUncaughtExceptionHandler> exceptionHandler) {
        Set<Class<? extends Annotation>> asyncAnnotationTypes = new LinkedHashSet<>(2);
        asyncAnnotationTypes.add(Async.class);
        try {
            asyncAnnotationTypes.add((Class<? extends Annotation>)
                    ClassUtils.forName("javax.ejb.Asynchronous", AsyncAnnotationAdvisor.class.getClassLoader()));
        }
        catch (ClassNotFoundException ex) {
            // If EJB 3.1 API not present, simply ignore.
        }
        //通知
        this.advice = buildAdvice(executor, exceptionHandler);
        //切入點(diǎn)
        this.pointcut = buildPointcut(asyncAnnotationTypes);
    }
  • 通知就是最終要執(zhí)行的。buildAdvice 用于構(gòu)建通知,主要是創(chuàng)建一個(gè) AnnotationAsyncExecutionInterceptor 類型的攔截器,并且配置好使用的執(zhí)行器和異常處理器。真正的異步執(zhí)行的代碼在 AsyncExecutionAspectSupport 中!
protected Advice buildAdvice(
            @Nullable Supplier&lt;Executor&gt; executor, @Nullable Supplier&lt;AsyncUncaughtExceptionHandler&gt; exceptionHandler) {
        AnnotationAsyncExecutionInterceptor interceptor = new AnnotationAsyncExecutionInterceptor(null);
        //配置攔截器
        interceptor.configure(executor, exceptionHandler);
        return interceptor;
    }
  • 配置攔截器,通過(guò)參數(shù)配置自定義的執(zhí)行器和異常處理器或者使用默認(rèn)的執(zhí)行器和異常處理器。
public void configure(@Nullable Supplier&lt;Executor&gt; defaultExecutor,
            @Nullable Supplier&lt;AsyncUncaughtExceptionHandler&gt; exceptionHandler) {
        //默認(rèn)執(zhí)行器
        this.defaultExecutor = new SingletonSupplier&lt;&gt;(defaultExecutor, () -&gt; getDefaultExecutor(this.beanFactory));
        this.exceptionHandler = new SingletonSupplier&lt;&gt;(exceptionHandler, SimpleAsyncUncaughtExceptionHandler::new);
    }
  • getDefaultExecutor() 方法,用來(lái)查找默認(rèn)的執(zhí)行器,父類 AsyncExecutionAspectSupport 首先尋找唯一一個(gè)類型為 TaskExecutor 的執(zhí)行器并返回,若存在多個(gè)則尋找默認(rèn)的執(zhí)行器 taskExecutor ,若無(wú)法找到則直接返回null。子類 AsyncExecutionInterceptor 重寫 getDefaultExecutor 方法,首先調(diào)用父類邏輯,返回null則配置一個(gè)名為 SimpleAsyncTaskExecutor 的執(zhí)行器
/**
 * 父類
 * 獲取或構(gòu)建此通知實(shí)例的默認(rèn)執(zhí)行器
 * 這里返回的執(zhí)行器將被緩存以供后續(xù)使用
 * 默認(rèn)實(shí)現(xiàn)搜索唯一的TaskExecutor的bean
 * 在上下文中,用于名為“taskExecutor”的Executor bean。
 * 如果兩者都不是可解析的,這個(gè)實(shí)現(xiàn)將返回 null
 */
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    if (beanFactory != null) {
        try {
            // 搜索唯一的一個(gè)TaskExecutor類型的bean并返回
            return beanFactory.getBean(TaskExecutor.class);
        }
        catch (NoUniqueBeanDefinitionException ex) {
            //找不到唯一一個(gè)bean異常后,搜索一個(gè)TaskExecutor類型的“taskExecutor”的bean并返回
            logger.debug("Could not find unique TaskExecutor bean", ex);
            try {
                return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
            }
            catch (NoSuchBeanDefinitionException ex2) {
                if (logger.isInfoEnabled()) {
                    logger.info("More than one TaskExecutor bean found within the context, and none is named " +
                            "'taskExecutor'. Mark one of them as primary or name it 'taskExecutor' (possibly " +
                            "as an alias) in order to use it for async processing: " + ex.getBeanNamesFound());
                }
            }
        }
        catch (NoSuchBeanDefinitionException ex) {
            //未找到異常時(shí)搜索一個(gè)TaskExecutor類型的“taskExecutor”的bean并返回
            logger.debug("Could not find default TaskExecutor bean", ex);
            try {
                return beanFactory.getBean(DEFAULT_TASK_EXECUTOR_BEAN_NAME, Executor.class);
            }
            catch (NoSuchBeanDefinitionException ex2) {
                logger.info("No task executor bean found for async processing: " +
                        "no bean of type TaskExecutor and no bean named 'taskExecutor' either");
            }
            // Giving up -&gt; either using local default executor or none at all...
        }
    }
    return null;
}
/**
 * 子類
 * 如父類為null則重新實(shí)例化一個(gè)名為SimpleAsyncTaskExecutor的執(zhí)行器
 */
@Override
@Nullable
protected Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {
    Executor defaultExecutor = super.getDefaultExecutor(beanFactory);
    return (defaultExecutor != null ? defaultExecutor : new SimpleAsyncTaskExecutor());
}

所以,到了這一步就可以理解為什么異步線程名默認(rèn)叫 SimpleAsyncTaskExecutor-xx ,為什么有了自己的線程池有可能異步用到了自己的線程池配置。

我們有這個(gè)切入點(diǎn)之后,每次請(qǐng)求接口執(zhí)行異步方法前都會(huì)執(zhí)行 AsyncExecutionInterceptor#invoke() , determineAsyncExecutor 用來(lái)決策使用哪個(gè)執(zhí)行器

@Nullable
protected AsyncTaskExecutor determineAsyncExecutor(Method method) {
    //在緩存的執(zhí)行器中選擇一個(gè)對(duì)應(yīng)方法的執(zhí)行器
    AsyncTaskExecutor executor = (AsyncTaskExecutor)this.executors.get(method);
    if (executor == null) {
        //獲取@Async注解中的value(指定的執(zhí)行器)
        String qualifier = this.getExecutorQualifier(method);
        Executor targetExecutor;
        if (StringUtils.hasLength(qualifier)) {
            //獲取指定執(zhí)行器的bean
            targetExecutor = this.findQualifiedExecutor(this.beanFactory, qualifier);
        } else {
            //選擇默認(rèn)的執(zhí)行器
            targetExecutor = (Executor)this.defaultExecutor.get();
        }
        if (targetExecutor == null) {
            return null;
        }
        executor = targetExecutor instanceof AsyncListenableTaskExecutor ? (AsyncListenableTaskExecutor)targetExecutor : new TaskExecutorAdapter(targetExecutor);
        //將執(zhí)行器進(jìn)行緩存
        this.executors.put(method, executor);
    }
    return (AsyncTaskExecutor)executor;
}

當(dāng)有了執(zhí)行器調(diào)用 doSubmit 方法將任務(wù)加入到執(zhí)行器中。

異步任務(wù),默認(rèn)將采用SimpleAsyncTaskExecutor作為執(zhí)行器!它有如下特點(diǎn):

不復(fù)用線程,也就是說(shuō)為每個(gè)任務(wù)新起一個(gè)線程。但是可以通過(guò) concurrencyLimit 屬性來(lái)控制并發(fā)線程數(shù)量,但是默認(rèn)情況下不做限制( concurrencyLimit 取值為-1)。
因此,如果我們使用異步任務(wù),一定不能采用默認(rèn)執(zhí)行器的配置,以防OOM異常!最好的方式是指定執(zhí)行器!

總結(jié)

本文主要以看源碼的方式來(lái)了解異步注解 @Async 是如何在項(xiàng)目中選擇線程以及使用線程的,盡量給異步任務(wù)指定一個(gè)獨(dú)有線程池,這樣會(huì)的避免不與其他業(yè)務(wù)共用線程池而造成影響。

以上就是Async的線程池使用選擇解析的詳細(xì)內(nèi)容,更多關(guān)于Async線程池使用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java獲取兩個(gè)字符串中最大相同子串的方法

    Java獲取兩個(gè)字符串中最大相同子串的方法

    今天小編就為大家分享一篇Java獲取兩個(gè)字符串中最大相同子串的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Java反射機(jī)制(Reflection)淺析

    Java反射機(jī)制(Reflection)淺析

    這篇文章主要介紹了Java反射機(jī)制(Reflection)淺析,本文以實(shí)例講解Java的反射機(jī)制,需要的朋友可以參考下
    2014-07-07
  • Java簡(jiǎn)單統(tǒng)計(jì)字符串中漢字,英文字母及數(shù)字?jǐn)?shù)量的方法

    Java簡(jiǎn)單統(tǒng)計(jì)字符串中漢字,英文字母及數(shù)字?jǐn)?shù)量的方法

    這篇文章主要介紹了Java簡(jiǎn)單統(tǒng)計(jì)字符串中漢字,英文字母及數(shù)字?jǐn)?shù)量的方法,涉及java針對(duì)字符串的遍歷、編碼轉(zhuǎn)換、判斷等相關(guān)操作技巧,需要的朋友可以參考下
    2017-06-06
  • Spring MVC 攔截器 interceptor 用法詳解

    Spring MVC 攔截器 interceptor 用法詳解

    這篇文章主要介紹了Spring MVC 攔截器 interceptor 用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 如何使用Java語(yǔ)言編寫打地鼠游戲全過(guò)程

    如何使用Java語(yǔ)言編寫打地鼠游戲全過(guò)程

    打地鼠是我們非常熟悉的一款小游戲,它的游戲結(jié)構(gòu)和規(guī)則也都比較簡(jiǎn)單,那么如果能夠親自徒手開(kāi)發(fā)這樣的一款經(jīng)典小游戲呢?這篇文章主要給大家介紹了關(guān)于如何使用Java語(yǔ)言編寫打地鼠游戲的相關(guān)資料,需要的朋友可以參考下
    2024-06-06
  • mybatis 事務(wù)回滾配置操作

    mybatis 事務(wù)回滾配置操作

    這篇文章主要介紹了mybatis 事務(wù)回滾配置操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-02-02
  • Java狀態(tài)機(jī)的一種優(yōu)雅寫法分享

    Java狀態(tài)機(jī)的一種優(yōu)雅寫法分享

    狀態(tài)機(jī)是一種數(shù)學(xué)模型,對(duì)于我們業(yè)務(wù)實(shí)現(xiàn)有很大的幫助。我們可以用非常多的方法實(shí)現(xiàn)狀態(tài)機(jī),這篇文章就來(lái)介紹一個(gè)狀態(tài)機(jī)優(yōu)雅的實(shí)現(xiàn)方法,希望對(duì)大家有所幫助
    2023-04-04
  • spring boot自定義配置源操作步驟

    spring boot自定義配置源操作步驟

    這篇文章主要介紹了spring boot自定義配置源操作步驟,需要的朋友可以參考下
    2017-10-10
  • 解決Springboot2.1.x配置Activiti7單獨(dú)數(shù)據(jù)源問(wèn)題

    解決Springboot2.1.x配置Activiti7單獨(dú)數(shù)據(jù)源問(wèn)題

    這篇文章主要介紹了Springboot2.1.x配置Activiti7單獨(dú)數(shù)據(jù)源問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-09-09
  • Java數(shù)組的定義、初始化、及二維數(shù)組用法分析

    Java數(shù)組的定義、初始化、及二維數(shù)組用法分析

    這篇文章主要介紹了Java數(shù)組的定義、初始化、及二維數(shù)組用法,結(jié)合具體實(shí)例形式分析了java數(shù)組概念、功能、數(shù)組定義、靜態(tài)數(shù)組、動(dòng)態(tài)數(shù)組、二維數(shù)組等相關(guān)使用技巧,需要的朋友可以參考下
    2019-01-01

最新評(píng)論

深水埗区| 汉沽区| 开江县| 平舆县| 阿坝| 滦平县| 汝州市| 嘉义县| 石阡县| 永吉县| 漠河县| 临汾市| 商丘市| 通江县| 新源县| 涞源县| 五华县| 通山县| 华安县| 上栗县| 绍兴县| 南通市| 达拉特旗| 武川县| 临潭县| 古浪县| 新化县| 大石桥市| 铜山县| 全州县| 铁岭县| 屏南县| 白城市| 高平市| 保康县| 藁城市| 乌苏市| 尖扎县| 织金县| 赤壁市| 辉南县|