90%的Java工程師都不瞭解的執行緒池細節問題!
公眾號後臺回覆 “ 資料 ”
獲取作者獨家祕製學習資料
執行緒池的工作原理
首先複習下執行緒池的基本原理, 我認為執行緒池它就是一個 排程任務 的工具。
眾所周知,在初始化執行緒池會給定執行緒池的大小,假設現在我們有 1000 個執行緒任務需要執行,而執行緒池的大小為 10~20。
在真正執行任務的過程中他肯定不會建立這1000個執行緒同時執行,而是充分利用執行緒池裡這 10~20 個執行緒來排程這1000個任務。
而這裡的 10~20 個執行緒最後會由執行緒池封裝為 ThreadPoolExecutor.Worker
物件,而這個 Worker
是實現了 Runnable 介面的,所以他自己本身就是一個執行緒。
深入分析
這裡我們來做一個模擬,建立了一個核心執行緒、最大執行緒數、阻塞佇列都為2的執行緒池。
這裡假設執行緒池已經完成了預熱,也就是執行緒池內部已經建立好了兩個執行緒 Worker
。
當我們往一個執行緒池丟一個任務會發生什麼事呢?
-
第一步是生產者,也就是任務提供者他執行了一個 execute() 方法,本質上就是往這個內部佇列裡放了一個任務。
-
之前已經建立好了的 Worker 執行緒會執行一個
while
迴圈 ---> 不停的從這個內部佇列
裡獲取任務。(這一步是競爭的關係,都會搶著從佇列裡獲取任務,由這個佇列內部實現了執行緒安全) -
獲取得到一個任務後,其實也就是拿到了一個
Runnable
物件(也就是execute(Runnabletask)
這裡所提交的任務), 接著執行這個Runnable
的 run() 方法,而不是 start() 。 這點需要注意。後文分析原因。
結合原始碼來看:
從圖中其實就對應了剛才提到的二三兩步:
-
while
迴圈,從getTask()
方法中一直不停的獲取任務。 -
拿到任務後,執行它的 run() 方法。
這樣一個執行緒就排程完畢,然後再次進入迴圈從佇列裡取任務並不斷的進行排程。
再次解釋之前的問題
接下來回顧一下以前提到的問題: 導致一個執行緒沒有執行的根本原因是?
在單個執行緒的執行緒池中一旦丟擲了未被捕獲的異常時,執行緒池會回收當前的執行緒,並建立一個新的 Worker
它也會一直不斷的從佇列裡獲取任務來執行,但由於這是一個消費執行緒, 根本沒有生產者往裡邊丟任務 ,因此它會一直 waiting 在從佇列裡獲取任務處。
所以也就造成了線上的佇列沒有消費,業務執行緒池沒有執行的問題。
結合之前的那張圖來看:
這裡大家問的最多的一個點是, 為什麼會沒有生產者往裡面丟任務 ? 圖中不是明明畫的有一個 product
嘛?
這裡確實是有些不太清楚,再次強調一次: 圖中的 product 是往內部佇列裡寫訊息的生產者,並不是往這個 Consumer 所在的執行緒池中寫任務的生產者。
因為即便 Consumer
是一個單執行緒的執行緒池,它依然具有一個常規執行緒池所具備的所有條件:
-
Worker 排程執行緒,也就是執行緒池執行的執行緒;雖然只有一個。
-
內部的阻塞佇列;雖然長度只有1。
再次結合圖來看:
所以之前提到的【沒有生產者往裡邊丟任務】是指右圖放大後的那一塊,也就是內部佇列並沒有其他執行緒往裡邊丟任務執行 execute()
方法。
而一旦發生未捕獲的異常後, Worker1
被回收,順帶的它所排程的執行緒 task1
(這個task1 也就是在執行一個 while 迴圈消費左圖中的那個佇列) 也會被回收掉。
新建立的 Worker2
會取代 Worker1
繼續執行 while
迴圈從內部佇列裡獲取任務,但此時這個佇列就一直會是空的,所以也就是處於 Waiting
狀態。
為什是 run() 而不是 start() ?
問題搞清楚後來想想為什麼執行緒池在排程的時候執行的是 Runnable
的 run()
方法,而不是 start()
方法呢?
我相信大部分沒有看過原始碼的同學心中第一個印象就應該是執行的 start()
方法;
因為不管是學校老師,還是網上大牛講的都是隻有執行了 start()
方法後作業系統才會給我們建立一個獨立的執行緒來執行,而 run()
方法只是一個普通的方法呼叫。
而線上程池這個場景中卻恰好就是要利用它 只是一個普通方法呼叫。
回到我在文初中所提到的:我認為執行緒池它就是一個 排程任務 的工具。
假設這裡是呼叫的 Runnable
的 start
方法,那會發生什麼事情?
如果我們往一個核心、最大執行緒數為 2 的執行緒池裡丟了 1000 個任務, 那麼它會額外的建立 1000 個執行緒,同時每個任務都是非同步執行的,一下子就執行完畢了 。
這樣就沒法做到由這兩個 Worker
執行緒來排程這 1000 個任務,而只有當做一個同步阻塞的 run()
方法呼叫時才能滿足這個要求。
END
歡迎長按下圖關注公眾號 石杉的架構筆記 ,後臺回覆“ 資料 ”,獲取作者獨家祕製學習資料!
BAT架構經驗傾囊相授