11.線程池是怎樣執行任務的


 

 

 

線程池是怎樣執行任務的?

 

 

 

我曾經在一個面試中被問到,說說線程池是怎樣執行任務的,由於對這個知識點不是很清楚,我當時很緊張,回答的不好,因此面試完當天我就惡補了這個知識點,現在來重溫一下說到執行任務,我們就必須先談談提交任務。

提交任務的方式有兩種,一種是execute,還有一種是submit。

 

 

這兩種方式在前面的章節已經講過,它們的區別在於execute只能提交runnable任務,也就是無返回之任務。而submit既可以提交runnable部任務,也可以提交callable任務,也就是有返回值的任務。雖然兩者提交任務的種類有差異,但是他們最終處理任務的方法是相同的,都是threadpoolExecutor類的execute方法。

 

 

Execute方法的實現就在 threadpoolExecutor內中submit方法的實現,是在attributeexecuteService抽象類,

 

 

看看submit方法內部做了哪些操作,首先判斷任務是否為空,當任務為空時,拋一個空指針異常,當任務不為空,將任務轉為有返回值任務。然后調用 execute方法執行,該任務此處調用的是threadpoolExecutor類中的execute方法,最后返回future對象拿到結果,調用者再通過future對象拿到執行結果。

 

 

接下來重點講講execute方法。execute方法可分為三個部分來看,第一部分判斷核心線程數是否已滿,能否再加入新的核心線程。第二部分判斷任務隊列是否已滿,能否再加入新的任務。第三部分判斷線程池中的線程是否已滿,能否再加入新的線程。 

 

 

接下來以此來看這三個部分。

首先來看第一部分。

判斷核心限值數是否已滿,也就是這行代碼workaccountof方法的作用是獲取當年線程池中有多少線程,corepoolsize的屬性記錄了當前限制池中和多少核心線程,當池中的限制數小於核心限值數,也就是核心線程沒滿時,

 

 

添加核心線程並執行提交的任務,也就是這行代碼addworker方法的作用是往線程池中添加線程,該方法有兩個參數,第一個參數是指定給線程的任務,在線程剛創建的時候就可以指定任務,線程拿到任務以后就開始執行。第二個參數是指定該線程是否為核心線程,true代表是核心線程。false代表不是核心線程,我們知道成為核心線程就意味着即使空閑下來也不會被銷毀。 

 

 

當然單獨設置允許銷毀核心線程除外,addworker方法\我們就簡單的說完了。

線程添加成功會怎樣?線程添加成功,執行完任務這個流程就結束了,直接return這是添加成功的情況。

 

 

假設添加失敗,也就是核心線程以滿,繼續往下執行。

Ctl記錄着線程池狀態和池中有多少線程,這里獲取它的值是為后續流程做准備。

 

 

接下來看第二部分,

當核心線程已滿時,我們就要考慮將任務先放入任務隊列中,此時要判斷一下任務隊列是否已滿,isrunning判斷線程池是否還在運行,如果沒在運行,好,直接跳過這個流程,進入下一個流程。如果線程池還在運行,

 

 

將任務添加至任務隊列中,

 

 

也就是調用workerqueue的offer方法,如果添加成功,任務將在隊列中等待被執行,看見流程圖是怎樣的,任務隊列沒滿,任務添加成功,等待被執行,這就是隊列沒滿的情況。 

 

 

來看左邊的代碼部分,if語句里面還有任務添加后的操作,再次獲取線程池狀態,

 

 

檢查線程池是否還在運行,如果沒運行就從隊列中移除任務,然后拒絕任務,

 

 

如果線程池還在運行,那么就檢查池中的線程數量是否為0,也就是池中已經沒有可用的線程,那么就添加一個非核心線程。

 

 

第二部分看完再來看

第三部分,

當任務隊列已滿時,我們就需要考慮往線程池中添加線程的,

 

 

如果池中的線程沒滿,那么就直接添加線程並執行提交的任務,這是一種結果。

 

 

還有一種結果是當池中的線程已滿,無法再往里面添加新線程時,我們只有將任務拒絕掉,

 

 

 

 

 

這是第三部分看完。我們已經畫完整個流程圖,也已看完代碼執行過程,應該來說代碼搭配着流程圖一起看,這個過程理解起來更容易。 


 

 

總結

最后總結一下本節內容,本節介紹了線程池執行任務的流程,如圖所示,以后在面試中遇到類似的問題就可以輕松應對了,甚至只看execute的方法,源代碼能否在紙上將其流程圖畫出來,看自己掌握了多少流程圖。 

 

 

 

 

  

附錄:

筆記完整文本:

我曾經在一個面試中被問到,說說線程池是怎樣執行任務的,由於對這個知識點不是很清楚,我當時很緊張,回答的不好,因此面試完當天我就惡補了這個知識點,現在來重溫一下說到執行任務,我們就必須先談談提交任務,提交任務的方式有兩種,一種是execute,還有一種是submit。這兩種方式在前面的章節已經講過,它們的區別在於execute只能提交runnable任務,也就是無返回之任務。而submit既可以提交runnable部任務,也可以提交callable任務,也就是有返回值的任務。雖然兩者提交任務的種類有差異,但是他們最終處理任務的方法是相同的,都是threadpoolExecutor類的execute方法。 Execute方法的實現就在 threadpoolExecutor內中submit方法的實現,是在attributeexecuteService抽象類,看看submit方法內部做了哪些操作,首先判斷任務是否為空,當任務為空時,拋一個空指針異常,當任務不為空,將任務轉為有返回值任務。然后調用 execute方法執行,該任務此處調用的是threadpoolExecutor類中的execute方法,最后返回future對象拿到結果,調用者再通過future對象拿到執行結果。接下來重點講講execute方法。execute方法可分為三個部分來看,第一部分判斷核心線程數是否已滿,能否再加入新的核心線程。第二部分判斷任務隊列是否已滿,能否再加入新的任務。第三部分判斷線程池中的線程是否已滿,能否再加入新的線程。 接下來以此來看這三個部分。首先來看第一部分。判斷核心限值數是否已滿,也就是這行代碼workaccountof方法的作用是獲取當年線程池中有多少線程,corepoolsize的屬性記錄了當前限制池中和多少核心線程,當池中的限制數小於核心限值數,也就是核心線程沒滿時,添加核心線程並執行提交的任務,也就是這行代碼addworker方法的作用是往線程池中添加線程,該方法有兩個參數,第一個參數是指定給線程的任務,在線程剛創建的時候就可以指定任務,線程拿到任務以后就開始執行。第二個參數是指定該線程是否為核心線程,true代表是核心線程。false代表不是核心線程,我們知道成為核心線程就意味着即使空閑下來也不會被銷毀。 當然單獨設置允許銷毀核心線程除外,addworker方法\我們就簡單的說完了,線程添加成功會怎樣?線程添加成功,執行完任務這個流程就結束了,直接return這是添加成功的情況。假設添加失敗,也就是核心線程以滿,繼續往下執行。Ctl記錄着線程池狀態和池中有多少線程,這里獲取它的值是為后續流程做准備。接下來看第二部分,當核心線程已滿時,我們就要考慮將任務先放入任務隊列中,此時要判斷一下任務作業是否已滿,isrunning判斷線程池是否還在運行,如果沒在運行,好,直接跳過這個流程,進入下一個流程。如果線程值還在運行,將任務添加至任務隊列中,也就是調用workerqueue的offer方法,如果添加成功,任務將在隊列中等待被執行,看見流程圖是怎樣的,任務隊列沒滿,任務添加成功,等待被執行,這就是對你沒滿的情況。 來看左邊的代碼部分,if語句里面還有任務添加后的操作,再次獲取線程池狀態,檢查線程池是否還在運行,如果沒運行就從隊列中移除任務,然后拒絕任務,如果線程池還在運行,那么就檢查池中的線程數量是否為0,也就是池中已經沒有可用的線程,那么就添加一個非核心線程。第二部分看完再來看第三部分,當任務對聯已滿時,我們就需要考慮往縣城池中添加線程的,如果池中的線程沒滿,那么就直接添加線程並執行提交的任務,這是一種結果。還有一種結果是當池中的線程已滿,無法再往里面添加新線程時,我們只有將任務拒絕掉,這是第三部分看完。我們已經畫完整個流程圖,也已看完代碼執行過程,應該來說代碼搭配着流程圖一起看,這個過程理解起來更容易。 最后總結一下本節內容,本節介紹了線程池執行任務的流程,如圖所示,以后在面試中遇到類似的問題就可以輕松應對了,甚至只看execute的方法,源代碼能否在紙上將其流程圖畫出來,看自己掌握了多少流程圖。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM