自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

淺析Spring中Async注解底層異步線程池原理

開發(fā) 前端
? Spring創(chuàng)建SimpleAsyncTaskExecutor實(shí)例來處理Async注解的異步任務(wù),SimpleAsyncTaskExecutor不是一個(gè)好的線程池實(shí)現(xiàn)類,SimpleAsyncTaskExecutor根據(jù)需要在當(dāng)前線程或者新線程中執(zhí)行異步任務(wù)。

一、前言

開發(fā)中我們經(jīng)常會用到異步方法調(diào)用,具體到代碼層面,異步方法調(diào)用的實(shí)現(xiàn)方式有很多種,比如最原始的通過實(shí)現(xiàn)Runnable接口或者繼承Thread類創(chuàng)建異步線程,然后啟動異步線程;再如,可以直接用java.util.concurrent包提供的線程池相關(guān)API實(shí)現(xiàn)異步方法調(diào)用。

如果說可以用一行代碼快速實(shí)現(xiàn)異步方法調(diào)用,那是不是比上面方法香很多。

Spring提供了Async注解,就可以幫助我們一行代碼搞定異步方法調(diào)用。Async注解用起來是很爽,但是如果不對其底層實(shí)現(xiàn)做深入研究,難免有時(shí)候也會心生疑慮,甚至?xí)蚴褂貌划?dāng),遇見一些讓人摸不著頭腦的問題。

本文首先將對Async注解做簡單介紹,然后和大家分享一個(gè)我們項(xiàng)目中因Async注解使用不當(dāng)?shù)木€上問題,接著再深扒Spring源碼,對Async注解底層異步線程池的實(shí)現(xiàn)原理一探究竟。

二、Async注解簡介

Async注解定義源碼

圖片圖片

從源碼可以看出@Async注解定義很簡單,只需要關(guān)注兩點(diǎn):

  • Target({ElementType.TYPE, ElementType.METHOD})標(biāo)志Async注解可以作用在方法和類上,作用在類上時(shí),類的所有方法可以實(shí)現(xiàn)異步調(diào)用。
  • String value( ) default ""是唯一字段屬性,用來指定異步線程池,且該字段有缺省值。

Async注解異步調(diào)用實(shí)現(xiàn)原理概述

在Spring框架中,Async注解的實(shí)現(xiàn)是通過AOP來實(shí)現(xiàn)的。具體來說,Async注解是由AsyncAnnotationAdvisor這個(gè)切面類來實(shí)現(xiàn)的。

AsyncAnnotationAdvisor類是Spring框架中用于處理Async注解的切面,它會在被Async注解標(biāo)識的方法被調(diào)用時(shí),創(chuàng)建一個(gè)異步代理對象來執(zhí)行方法。這個(gè)異步代理對象會在一個(gè)新的線程中調(diào)用被@Async注解標(biāo)識的方法,從而實(shí)現(xiàn)方法的異步執(zhí)行。

在AsyncAnnotationAdvisor中,會使用AsyncExecutionInterceptor來處理Async注解。AsyncExecutionInterceptor是實(shí)現(xiàn)了MethodInterceptor接口的類,用于攔截被Async注解標(biāo)識的方法的調(diào)用,并在一個(gè)新的線程中執(zhí)行這個(gè)方法。

通過AOP的方式實(shí)現(xiàn)Async注解的異步執(zhí)行,Spring框架可以在方法調(diào)用時(shí)動態(tài)地創(chuàng)建代理對象來實(shí)現(xiàn)異步執(zhí)行,而不需要在業(yè)務(wù)代碼中顯式地創(chuàng)建新線程。

總的來說,Async注解的實(shí)現(xiàn)是通過AOP機(jī)制來實(shí)現(xiàn)的,具體的切面類是AsyncAnnotationAdvisor,它利用AsyncExecutionInterceptor來處理被Async注解標(biāo)識的方法的調(diào)用,實(shí)現(xiàn)方法的異步執(zhí)行。

三、Async注解底層異步線程池原理探究

獲取Async注解線程池主流程解析

進(jìn)入到Spring源碼Async注解AOP切面實(shí)現(xiàn)部分,我們重點(diǎn)剖析異步調(diào)用實(shí)現(xiàn)中線程池是怎么處理的。下圖是org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke方法的實(shí)現(xiàn),可以看出是調(diào)用determineAsyncExecutor方法獲取異步線程池。

AsyncExecutionInterceptor#invokeAsyncExecutionInterceptor#invoke

下圖是determineAsyncExecutor方法實(shí)現(xiàn):

圖片圖片

圖片圖片

左圖為AsyncExecutionInterceptor#determineAsyncExecutor,右圖為AsyncExecutionAspectSupport#getExecutorQualifier

從代碼實(shí)現(xiàn)中可以看到determineAsyncExecutor獲取線程池的大致流程:

determineAsyncExecutor獲取線程池流程determineAsyncExecutor獲取線程池流程

如果在使用Async注解時(shí)指定了自定義線程池比較好理解,如果使用Async注解時(shí)沒有指定自定義線程池,Spring是怎么處理默認(rèn)線程池呢?繼續(xù)深入源碼看看Spring提供的默認(rèn)線程池的實(shí)現(xiàn)。

Spring是怎么為Async注解提供默認(rèn)線程池的

Async注解默認(rèn)線程池有下面兩個(gè)方法實(shí)現(xiàn):   

  • org.springframework.aop.interceptor.AsyncExecutionInterceptor#getDefaultExecutor
  • org.springframework.aop.interceptor.AsyncExecutionAspectSupport#getDefaultExecutor

AsyncExecutionInterceptor#getDefaultExecutorAsyncExecutionInterceptor#getDefaultExecutor

可以看出AsyncExecutionInterceptor#getDefaultExecutor方法比較簡單:先嘗試調(diào)用父類AsyncExecutionAspectSupport#getDefaultExecutor方法獲取線程池,如果父類方法獲取不到線程池再用創(chuàng)建SimpleAsyncTaskExecutor對象作為Async的線程池返回。

AsyncExecutionAspectSupport#getDefaultExecutorAsyncExecutionAspectSupport#getDefaultExecutor

再來看父類AsyncExecutionAspectSupport#getDefaultExecutor方法的實(shí)現(xiàn),可以看到Spring根據(jù)類型從Spring容器中獲取TaskExecutor類的實(shí)例,先記住這個(gè)關(guān)鍵點(diǎn)。

我們知道,Spring根據(jù)類型獲取實(shí)例時(shí),如果spring容器中有且只有一個(gè)指定類型的實(shí)例對象,會直接返回,否則的話,會拋出NoUniqueBeanDefinitionException異?;蛘逳oSuchBeanDefinitionException異常。

但是,對于Executor類型,Spring容器卻“網(wǎng)開一面”,有一個(gè)特殊處理:當(dāng)從Spring容器中獲取Executor實(shí)例對象時(shí),如果滿足@ConditionalOnMissingBean(Executor.class)條件,Spring容器會自動裝載一個(gè)ThreadPoolTaskExecutor實(shí)例對象,而ThreadPoolTaskExecutor是TaskExecutor的實(shí)現(xiàn)類。

圖片圖片

圖片圖片

左圖為TaskExecutionAutoConfiguration,右圖為TaskExecutionProperties

從TaskExecutionProperties和TaskExecutionAutoConfiguration兩個(gè)配置類我們看到Spring自動裝載的ThreadPoolTaskExecutor線程池對象的參數(shù):核心線程數(shù)=8;最大線程數(shù)=Integer.MAX_VALUE;隊(duì)列大小=Integer.MAX_VALUE。

四、總結(jié)

現(xiàn)在Async注解線程池源碼已經(jīng)看的差不多了,下面這張圖是Spring處理Async異步線程池的流程:

Async異步線程池獲取流程Async異步線程池獲取流程

歸納一下:如果在使用Async注解時(shí)沒有指定自定義的線程池會出現(xiàn)以下幾種情況:

  • 當(dāng)Spring容器中有且僅有一個(gè)TaskExecutor實(shí)例時(shí),Spring會用這個(gè)線程池來處理Async注解的異步任務(wù),這可能會踩坑,如果這個(gè)TaskExecutor實(shí)例是第三方j(luò)ar引入的,可能會出現(xiàn)很詭異的問題。
  • Spring創(chuàng)建一個(gè)核心線程數(shù)=8、最大線程數(shù)=Integer.MAX_VALUE、隊(duì)列大小=Integer.MAX_VALUE的線程池來處理Async注解的異步任務(wù),這時(shí)候也可能會踩坑,由于線程池參數(shù)設(shè)置不合理,核心線程數(shù)=8,隊(duì)列大小過大,如果有大批量并發(fā)任務(wù),可能會出現(xiàn)OOM。
  • Spring創(chuàng)建SimpleAsyncTaskExecutor實(shí)例來處理Async注解的異步任務(wù),SimpleAsyncTaskExecutor不是一個(gè)好的線程池實(shí)現(xiàn)類,SimpleAsyncTaskExecutor根據(jù)需要在當(dāng)前線程或者新線程中執(zhí)行異步任務(wù)。如果當(dāng)前線程已經(jīng)有空閑線程可用,任務(wù)將在當(dāng)前線程中執(zhí)行,否則將創(chuàng)建一個(gè)新線程來執(zhí)行任務(wù)。由于這個(gè)線程池沒有線程管理的能力,每次提交任務(wù)都實(shí)時(shí)創(chuàng)建新城,所以如果任務(wù)量大,會導(dǎo)致性能下降。
責(zé)任編輯:武曉燕 來源: 得物技術(shù)
相關(guān)推薦

2024-12-17 00:00:00

Spring線程

2018-06-21 14:46:03

Spring Boot異步調(diào)用

2020-11-05 11:14:29

Docker底層原理

2009-07-22 09:39:18

CLR線程池

2023-02-12 23:23:30

2024-07-12 14:46:20

2024-03-28 12:51:00

Spring異步多線程

2022-09-28 14:54:07

Spring注解方式線程池

2009-08-21 11:31:59

異步和多線程的區(qū)別

2023-07-31 08:05:30

Spring任務(wù)調(diào)度

2025-04-08 00:00:00

@AsyncSpring異步

2024-08-22 10:39:50

@Async注解代理

2024-05-07 08:23:03

Spring@Async配置

2021-11-11 15:25:28

@AsyncJava線程池

2022-11-15 08:10:23

SpringMyBatis底層

2024-10-14 13:12:59

2012-05-15 02:18:31

Java線程池

2020-12-10 08:24:40

線程池線程方法

2009-07-09 10:28:19

線程池JDK5

2024-03-06 07:52:21

Spring框架響應(yīng)式編程微服務(wù)架構(gòu)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號