當前位置:首頁 » 操作系統 » asynctask源碼

asynctask源碼

發布時間: 2025-03-05 07:22:26

⑴ 為什麼都不建議直接使用 @Async 註解實現非同步

對於非同步方法調用,從 Spring3 開始提供了 @Async 註解,該註解可以被標注在方法上,以便非同步地調用該方法。調用者將在調用時立即返回,方法的實際執行將提交給 Spring TaskExecutor 的任務中,由指定的線程池中的線程執行。

在實際項目中, 使用 @Async 調用線程池,推薦等方式是是使用自定義線程池的模式,自定義線程池常用的方案:重新實現 AsyncConfigurer 介面。

場景

同步: 同步就是整個處理過程順序執行,當各個過程都執行完畢,並返回結果。

非同步: 非同步調用則是只是發送了調用的指令,調用者無需等待被調用的方法完全執行完畢;而是繼續執行下面的流程。例如, 在某個調用中,需要順序調用 A, B, C 三個過程方法;如他們都是同步調用,則需要將他們都順序執行完畢之後,方算作過程執行完畢;如 B 為一個非同步的調用方法,則在執行完 A 之後,調用 B,並不等待 B 完成,而是執行開始調用 C,待 C 執行完畢之後,就意味著這個過程執行完畢了。在 java 中,一般在處理類似的場景之時,都是基於創建獨立的線程去完成相應的非同步調用邏輯,通過主線程和不同的業務子線程之間的執行流程,從而在啟動獨立的線程之後,主線程繼續執行而不會產生停滯等待的情況。

Spring 已經實現的線程池

SimpleAsyncTaskExecutor:不是真的線程池,這個類不重用線程,默認每次調用都會創建一個新的線程

SyncTaskExecutor:這個類沒有實現非同步調用,只是一個同步操作。只適用於不需要多線程的地方。

ConcurrentTaskExecutor:Executor 的適配類,不推薦使用。如果 ThreadPoolTaskExecutor 不滿足要求時,才用考慮使用這個類。

SimpleThreadPoolTaskExecutor:是 Quartz 的 SimpleThreadPool 的類。線程池同時被 quartz 和非 quartz 使用,才需要使用此類。

ThreadPoolTaskExecutor :最常使用,推薦。其實質是對 java.util.concurrent.ThreadPoolExecutor 的包裝。

常見的非同步方式有:

最簡單的非同步調用,返回值為 void。

帶參數的非同步調用,非同步方法可以傳入參數。

存在返回值,常調用返回 Future/CompletableFuture。

@Async 應用默認線程池

Spring 應用默認的線程池,指在 @Async 註解在使用時,不指定線程池的名稱。查看源碼,@Async 的默認線程池為 SimpleAsyncTaskExecutor。

無返回值的非同步調用@Override@Async("taskExecutor")public void pageExportOrderBigExcel(HttpServletResponse response, JSONObject queryConditionDataJson, SdSchoolFilterConfig sdSchoolFilterConfig, LoginUser loginUser, SdSchoolDataExportTaskRecord exportTask, HttpServletRequest request, String tenantId) {try {Thread.sleep(1000);exportTask.setExportTaskStartTime(new Date());sdSchoolOrderService.exportOrderBigExcelPage(response, queryConditionDataJson, exportTask, sdSchoolFilterConfig.getFilterName(), loginUser, request, tenantId);exportTask.setExportTaskEndTime(new Date());exportTaskRecordService.updateById(exportTask);} catch (Exception e) {log.error("訂單數據分頁導出失敗", e); }}默認線程池的弊端

在線程池應用中,參考阿里巴巴 java 開發規范:線程池不允許使用 Executors 去創建,不允許使用系統默認的線程池,推薦通過 ThreadPoolExecutor 的方式,這樣的處理方式讓開發的工程師更加明確線程池的運行規則,規避資源耗盡的風險。Executors 各個方法的弊端:

newFixedThreadPool 和 newSingleThreadExecutor:主要問題是堆積的請求處理隊列可能會耗費非常大的內存,甚至 OOM。

newCachedThreadPool 和 newScheledThreadPool:要問題是線程數最大數是 Integer.MAX_VALUE,可能會創建數量非常多的線程,甚至 OOM。

@Async 默認非同步配置使用的是 SimpleAsyncTaskExecutor,該線程池默認來一個任務創建一個線程,若系統中不斷的創建線程,最終會導致系統佔用內存過高,引發 OutOfMemoryError 錯誤。針對線程創建問題,SimpleAsyncTaskExecutor 提供了限流機制,通過 concurrencyLimit 屬性來控制開關,當 concurrencyLimit>=0 時開啟限流機制,默認關閉限流機制即 concurrencyLimit=-1,當關閉情況下,會不斷創建新的線程來處理任務。基於默認配置,SimpleAsyncTaskExecutor 並不是嚴格意義的線程池,達不到線程復用的功能。

@Async 應用自定義線程池

自定義線程池,可對系統中線程池更加細粒度的控制,方便調整線程池大小配置,線程執行異常控制和處理。在設置系統自定義線程池代替默認線程池時,雖可通過多種模式設置,但替換默認線程池最終產生的線程池有且只能設置一個(不能設置多個類繼承 AsyncConfigurer)。自定義線程池有如下方式:

重新實現介面 AsyncConfigurer;

繼承 AsyncConfigurerSupport;

配置由自定義的 TaskExecutor 替代內置的任務執行器。

通過查看 Spring 源碼關於 @Async 的默認調用規則,會優先查詢源碼中實現 AsyncConfigurer 這個介面的類,實現這個介面的類為 AsyncConfigurerSupport。但默認配置的線程池和非同步處理方法均為空,所以,無論是繼承或者重新實現介面,都需指定一個線程池。且重新實現 public Executor getAsyncExecutor () 方法。

實現介面 AsyncConfigurer@Configuration public class AsyncConfiguration implements AsyncConfigurer { @Bean("taskExecutor") public ThreadPoolTaskExecutor executor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); int corePoolSize = 10; executor.setCorePoolSize(corePoolSize); int maxPoolSize = 50; executor.setMaxPoolSize(maxPoolSize); int queueCapacity = 10; executor.setQueueCapacity(queueCapacity); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executor.setThreadNamePrefix( "asyncServiceExecutor-"); executor.(true); executor.setAwaitTerminationSeconds(awaitTerminationSeconds); executor.initialize(); return executor; } @Override public Executor getAsyncExecutor() { return executor(); } }繼承 @EnableAsyncclass SpringAsyncConfigurer extends AsyncConfigurerSupport {@Beanpublic ThreadPoolTaskExecutor asyncExecutor() {ThreadPoolTaskExecutor threadPool = new ThreadPoolTaskExecutor();threadPool.setCorePoolSize(3);threadPool.setMaxPoolSize(3);threadPool.(true);threadPool.setAwaitTerminationSeconds(60 * 15);return threadPool;}@Overridepublic Executor getAsyncExecutor() {return asyncExecutor;}}配置自定義的 TaskExecutor (建議採用方式)/** * 線程池參數配置,多個線程池實現線程池隔離,@Async註解,默認使用系統自定義線程池,可在項目中設置多個線程池,在非同步調用的時候,指明需要調用的線程池名稱,比如:@Async("taskName") * * @author: jacklin * @since: 2021/5/18 11:44 **/@EnableAsync@Configurationpublic class TaskPoolConfig {/** * 非同步導出 * * @author: jacklin * @since: 2021/11/16 17:41 **/@Bean("taskExecutor")public Executor taskExecutor() {//返回可用處理器的Java虛擬機的數量 12int i = Runtime.getRuntime().availableProcessors();System.out.println("系統最大線程數: " + i);ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();//核心線程池大小executor.setCorePoolSize(16);//最大線程數executor.setMaxPoolSize(20);//配置隊列容量,默認值為Integer.MAX_VALUEexecutor.setQueueCapacity(99999);//活躍時間executor.setKeepAliveSeconds(60);//線程名字前綴executor.setThreadNamePrefix("asyncServiceExecutor -");//設置此執行程序應該在關閉時阻止的最大秒數,以便在容器的其餘部分繼續關閉之前等待剩餘的任務完成他們的執行executor.setAwaitTerminationSeconds(60);//等待所有的任務結束後再關閉線程池executor.(true);return executor;}}多個線程池

@Async 註解,使用系統默認或者自定義的線程池(代替默認線程池)。可在項目中設置多個線程池,在非同步調用時,指明需要調用的線程池名稱,如 @Async ("new_taskName")。

原文:https://juejin.cn/post/7099328896142671903

⑵ UE4 多線程源碼淺析(2——AsyncTask)

深入探討虛幻引擎中的多線程系統,本文將重點解析AsyncTask模塊,以期對如何在虛幻引擎中高效地運用非同步任務有更深的理解。

在學習AsyncTask之前,我們首先需要理解虛幻引擎中的線程池機制。非同步任務系統正是基於線程池構建的,了解線程池的運作機制對於理解AsyncTask至關重要。

線程池分為基類FQueuedThreadPool與子類FQueuedThreadPoolBase,後者負責定義線程池的內部數據結構,如等待的任務隊列、正在執行任務的線程以及線程池中所有線程等。FQueuedThreadPoolBase類通過實現一些介面,如Create、Destroy、AddQueuedWork等,來控制線程池的創建、銷毀與任務的調度。

任務(IQueuedWork)通過繼承介面類實現特定功能,包括DoThreadedWork與Abandon等,分別用於執行任務與放棄隊列中的任務。任務執行時,線程池中的線程(FQueuedThread)負責調用任務介面,執行具體操作。

在引擎啟動時,虛幻線程池在FEngineLoop::PreInitPreStartupScreen中進行初始化,通過FQueuedThreadPool::Allocate創建三個線程池實例,分別為GThreadPool、GBackgroundPriorityThreadPool與GIOThreadPool,分別用於普通任務、優先順序任務與IO操作。

接下來,我們深入探討線程池的創建、添加任務與線程獲取任務的實現細節。線程池的創建通過FQueuedThreadPool::Allocate完成,初始化線程池指定數量的線程。添加任務時,使用AddQueuedWork介面,確保線程池中的線程能夠高效地獲取與執行任務。線程獲取任務的方式則通過ReturnToPoolOrGetNextJob介面實現,確保線程池的高效運行與任務的合理調度。

在理解了線程池機制與任務調度原理後,我們轉向AsyncTask的解析。AsyncTask作為IQueuedWork的子類,用於實現非同步任務的啟動與管理。通過FAutoDeleteAsyncTask與FAsyncTask兩個類,我們可以更好地理解如何在虛幻引擎中利用非同步任務,提升應用性能與用戶體驗。

AsyncTask的實現細節涉及任務的啟動、執行與取消等操作,以及線程池的選擇與任務調度策略的優化。了解這些細節將幫助開發者更高效地構建和管理非同步任務,實現復雜場景下的性能優化。

本文通過深入解析虛幻引擎中的線程池與AsyncTask模塊,旨在為開發者提供一套完整的多線程系統理解框架。了解這些內部機制不僅可以提升代碼質量,還能在實際項目開發中實現更高效、更靈活的資源管理與任務調度。

⑶ 在Android源碼中,AsyncTask中的onPostExecute方法是何時調用的

這個簡單, 一般要覆蓋三個方法, 1、onPreExecute(), 高負載代碼執行之前調用 ,通常用來顯示一個進度條,在主線程中執行 2、doInBackGround() : onPreExecute() 執行完後調用,此方法通常就是放高負載代碼的,比如遠程請求,巨大數據載入等,你不用新建線程來包裝此方法 AsyncTask(或子類)會自動在新線程中調用此方法 3、onPostExecute(Result), 在doInBackground完成之後調用,一般是設置結果,取消第一個方法顯示的進度條。 onProgressUpdate() 一般用來更新第一個方法顯示的進度條,什麼下載了50% 51% 。。。 總之,子類化AsyncTask 你不用顧及線程問題, 主線程中直接new AsyncTask的子類,並調用execute就行了,一定要在主線程中調execute。 還有,這些是AsyncTask的生命周期方法,你自己不要調用。

⑷ 在Android源碼中,AsyncTask中的onPostExecute方法是何時調用的

在doInBackground方法中,通過執行publishProgress方法call onProgressUpdate方法更新UI,這種方法常用來顯示進度。onPostExecute方法是在doInBackground方法執行之後執行。你可以寫一個介面層,然後把值傳入介面層。在UI層實現介面,就可以獲取到值。

⑸ Android中的線程狀態 - AsyncTask詳解

在操作系統中,線程是操作系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制地產生,並且 線程的創建和銷毀都會有相應的開銷。 當系統中存在大量的線程時,系統會通過會時間片輪轉的方式調度每個線程,因此線程不可能做到絕對的並行。

如果在一個進程中頻繁地創建和銷毀線程,顯然不是高效的做法。正確的做法是採用線程池,一個線程池中會緩存一定數量的線程,通過線程池就可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。

AsyncTask是一個抽象類,它是由Android封裝的一個輕量級非同步類(輕量體現在使用方便、代碼簡潔),它可以在線程池中執行後台任務,然後把執行的進度和最終結果傳遞給主線程並在主線程中更新UI。

AsyncTask的內部封裝了 兩個線程池 (SerialExecutor和THREAD_POOL_EXECUTOR)和 一個Handler (InternalHandler)。

其中 SerialExecutor線程池用於任務的排隊,讓需要執行的多個耗時任務,按順序排列 THREAD_POOL_EXECUTOR線程池才真正地執行任務 InternalHandler用於從工作線程切換到主線程

1.AsyncTask的泛型參數

AsyncTask是一個抽象泛型類。

其中,三個泛型類型參數的含義如下:

Params: 開始非同步任務執行時傳入的參數類型;

Progress: 非同步任務執行過程中,返回下載進度值的類型;

Result: 非同步任務執行完成後,返回的結果類型;

如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void來代替。

有了這三個參數類型之後,也就控制了這個AsyncTask子類各個階段的返回類型,如果有不同業務,我們就需要再另寫一個AsyncTask的子類進行處理。

2.AsyncTask的核心方法

onPreExecute()

這個方法會在 後台任務開始執行之間調用,在主線程執行。 用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。

doInBackground(Params...)

這個方法中的所有代碼都會 在子線程中運行,我們應該在這里去處理所有的耗時任務。

任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。 注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。

onProgressUpdate(Progress...)

當在後台任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。 在這個方法中可以對UI進行操作,在主線程中進行,利用參數中的數值就可以對界面元素進行相應的更新。

onPostExecute(Result)

當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中, 可以利用返回的數據來進行一些UI操作,在主線程中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。

上面幾個方法的調用順序:

onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()

如果不需要執行更新進度則為onPreExecute() --> doInBackground() --> onPostExecute(),

除了上面四個方法,AsyncTask還提供了onCancelled()方法, 它同樣在主線程中執行,當非同步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用 ,但是要注意的是, AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要在線程內部進行標記判斷然後中斷線程。

3.AsyncTask的簡單使用

這里我們模擬了一個下載任務,在doInBackground()方法中去執行具體的下載邏輯,在onProgressUpdate()方法中顯示當前的下載進度,在onPostExecute()方法中來提示任務的執行結果。如果想要啟動這個任務,只需要簡單地調用以下代碼即可:

4.使用AsyncTask的注意事項

①非同步任務的實例必須在UI線程中創建,即AsyncTask對象必須在UI線程中創建。

②execute(Params... params)方法必須在UI線程中調用。

③不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。

④不能在doInBackground(Params... params)中更改UI組件的信息。

⑤一個任務實例只能執行一次,如果執行第二次將會拋出異常。

先從初始化一個AsyncTask時,調用的構造函數開始分析。

這段代碼雖然看起來有點長,但實際上並沒有任何具體的邏輯會得到執行,只是初始化了兩個變數,mWorker和mFuture,並在初始化mFuture的時候將mWorker作為參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象,這兩個變數會暫時保存在內存中,稍後才會用到它們。 FutureTask實現了Runnable介面,關於這部分內容可以看這篇文章。

mWorker中的call()方法執行了耗時操作,即result = doInBackground(mParams);,然後把執行得到的結果通過postResult(result);,傳遞給內部的Handler跳轉到主線程中。在這里這是實例化了兩個變數,並沒有開啟執行任務。

那麼mFuture對象是怎麼載入到線程池中,進行執行的呢?

接著如果想要啟動某一個任務,就需要調用該任務的execute()方法,因此現在我們來看一看execute()方法的源碼,如下所示:

調用了executeOnExecutor()方法,具體執行邏輯在這個方法裡面:

可以 看出,先執行了onPreExecute()方法,然後具體執行耗時任務是在exec.execute(mFuture),把構造函數中實例化的mFuture傳遞進去了。

exec具體是什麼?

從上面可以看出具體是sDefaultExecutor,再追溯看到是SerialExecutor類,具體源碼如下:

終於追溯到了調用了SerialExecutor 類的execute方法。SerialExecutor 是個靜態內部類,是所有實例化的AsyncTask對象公有的,SerialExecutor 內部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務是串列執行的,即多個任務需要一個個加到該隊列中,然後執行完隊列頭部的再執行下一個,以此類推。

在這個方法中,有兩個主要步驟。

①向隊列中加入一個新的任務,即之前實例化後的mFuture對象。

②調用 scheleNext()方法,調用THREAD_POOL_EXECUTOR執行隊列頭部的任務。

由此可見SerialExecutor 類僅僅為了保持任務執行是串列的,實際執行交給了THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR又是什麼?

實際是個線程池,開啟了一定數量的核心線程和工作線程。然後調用線程池的execute()方法。執行具體的耗時任務,即開頭構造函數中mWorker中call()方法的內容。先執行完doInBackground()方法,又執行postResult()方法,下面看該方法的具體內容:

該方法向Handler對象發送了一個消息,下面具體看AsyncTask中實例化的Hanlder對象的源碼:

在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即執行完了doInBackground()方法並傳遞結果,那麼就調用finish()方法。

如果任務已經取消了,回調onCancelled()方法,否則回調 onPostExecute()方法。

如果收到的消息是MESSAGE_POST_PROGRESS,回調onProgressUpdate()方法,更新進度。

InternalHandler是一個靜態類,為了能夠將執行環境切換到主線程,因此這個類必須在主線程中進行載入。所以變相要求AsyncTask的類必須在主線程中進行載入。

到此為止,從任務執行的開始到結束都從源碼分析完了。

AsyncTask的串列和並行

從上述源碼分析中分析得到,默認情況下AsyncTask的執行效果是串列的,因為有了SerialExecutor類來維持保證隊列的串列。如果想使用並行執行任務,那麼可以直接跳過SerialExecutor類,使用executeOnExecutor()來執行任務。

四、AsyncTask使用不當的後果

1.)生命周期

AsyncTask不與任何組件綁定生命周期,所以在Activity/或者Fragment中創建執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);

2.)內存泄漏

3.) 結果丟失

屏幕旋轉或Activity在後台被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。

自己是從事了七年開發的Android工程師,不少人私下問我,2019年Android進階該怎麼學,方法有沒有?

沒錯,年初我花了一個多月的時間整理出來的學習資料,希望能幫助那些想進階提升Android開發,卻又不知道怎麼進階學習的朋友。【 包括高級UI、性能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料 】,希望能幫助到您面試前的復習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。

熱點內容
android屏幕位置 發布:2025-03-05 12:18:17 瀏覽:319
估算形演算法 發布:2025-03-05 12:12:41 瀏覽:42
c語言相除 發布:2025-03-05 12:00:08 瀏覽:856
c語言強製取整 發布:2025-03-05 11:50:05 瀏覽:599
php視頻源碼 發布:2025-03-05 11:30:48 瀏覽:623
編程報表 發布:2025-03-05 11:29:18 瀏覽:958
python面向對象編程指南 發布:2025-03-05 11:09:21 瀏覽:595
bat腳本判斷 發布:2025-03-05 10:58:58 瀏覽:632
連接資料庫的類 發布:2025-03-05 10:51:54 瀏覽:392
androidjswebview交互 發布:2025-03-05 10:51:47 瀏覽:120