Java執行緒池原始碼簡析
上一篇介紹了執行緒池中的幾種型別,本文來簡單分析一下執行緒池ThreadPoolExecutor的原始碼。
首先來看例項域

ctl:代表執行緒池的控制狀態,使用這個變數標識執行緒池的狀態,它的值通過runState與workCount同時確定。workCount為允許開啟並且不允許停止的執行緒數量,可以把它看做執行緒池中存活的執行緒數量。runState提供主要的生命週期控制,他有以下幾種狀態,RUNNING:執行中,可以接收新任務並處理排隊任務;SHUTDOWN:停止接收新任務,但是可以處理排隊中的任務;STOP:不接受新任務,不處理排隊任務,中斷正在進行中的任;,TIDYING:所有任務被終止,workCount為0,轉換到該狀態的執行緒將執行terminated()方法;TERMINATED:意味著terminated()方法執行完成。runState將會隨著執行時間的增長而改變,但不需要到達每個狀態。執行緒池的doc中給出了幾種可能的狀態轉換。RUNNING -> SHUTDOWN:呼叫了shutdown方法,或者finalize方法;(RUNNING or SHUTDOWN) -> STOP:呼叫了shutdownNow方法;SHUTDOWN -> TIDYING:佇列與執行緒池均為空時;STOP -> TIDYING:執行緒池為空時;TIDYING -> TERMINATED:terminated方法執行完成時。
execute與submit
我們使用執行緒池執行任務時是呼叫執行緒池的execute或者submit方法,後者可以獲取任務返回值,這倆個方法是執行緒池的執行入口,我們著重看一下execute方法,submit方法返回future物件基本也是相同的邏輯,就不貼程式碼了。

execute方法內部
這裡的commond是一個Runnable物件。首先使用workerCountOf(c)計算出執行緒數量是否小於核心執行緒數,若小於則呼叫addWorker嘗試新增任務,建立核心執行緒。再使用isRunning(c) &&workQueue.offer(command)判斷執行緒池執行狀態以及佇列能否新加任務,這裡的狀態就是執行緒池中執行緒數已達到核心執行緒數但還未達到最大執行緒數。接下來!isRunning(recheck) && remove(command)再次檢查執行緒池執行狀態(上次校驗已過期,防止併發),若執行緒池狀態為非running,則移除已經新增到佇列中的任務並執行拒絕策略。else if判斷workerCountOf(recheck) ==0執行緒池中工作執行緒是否為0,可能大家會有疑問,核心執行緒不是不能被回收的嗎,執行緒數怎麼會為0呢,其實執行緒池提供了allowCoreThreadTimeOut引數,當該值為true時(預設為false),當核心執行緒空閒時間超過keepLiveTime時,核心執行緒也將被回收,這時雖然池中沒有可用執行緒,但是任務同樣需要執行,也會呼叫addWorker方法。這裡的狀態就是核心執行緒已滿,將任務放入阻塞佇列。!addWorker(command,false)判斷是否阻塞佇列已滿並且是否超過了最大執行緒數,滿+超則執行拒絕策略。可以看到,他主要通過addWorker的倆個引數來區分各種情況,我們繼續來看addWorker方法。
addworker


第一個引數firstTask表示新執行緒應該首先執行的任務(如果沒有,則為null)。第二個引數core代表是否使用核心執行緒。
首先判斷執行緒池狀態是否為非執行狀態,若是直接拒絕返回false。第二個判斷根據core的值判斷執行緒數是否大於核心或最大執行緒數,若是直接返回false。
進入程式碼下半部分則可以正常的新增任務了。首先將任務封裝成worker,這是執行緒池內部對於Runnable的封裝

可以看到他只是對runnable和thread進行了簡單封裝,主要方法run也只是呼叫了runWorker方法(後面會說)。繼續addWorker方法的分析,封裝為worker後,進行加鎖控制將任務加入到hashset中並啟動worker中執行緒,由worker原始碼可以發現,執行緒工廠在建立執行緒thread時,將Woker作為引數傳入,當執行start方法啟動執行緒thread時,因為worker實現runnable方法,所以其實是執行了Worker的runWorker方法。執行後最終返回執行緒啟動結果。接下來繼續分析runWorker方法
runWorker

task不為空時直接執行該task,為空時直接呼叫getTask從阻塞佇列中獲取。這裡有個擴充套件點就是任務執行執行前後會有beforeExecute和afterExecute的回撥,用於執行自定義程式碼。
結尾
到這裡執行緒池大體的執行邏輯就大致介紹完了,我個人覺得看原始碼的時候不要深陷於細節中,因為庫函式要考慮的東西太多,而且大神的程式碼也不是我等凡人能輕鬆理解的。。清楚很重要,具體的細節有精力的話可以深入研究。