通過圖表和流程圖來精簡解析線程池的各種參數配置和基本的工作原理. 更基礎的一些原理, 例如condition的等待和通知等會單獨再開一篇講解.
參數解析
理解了ThreadPoolExecutor每個構造參數的含義和作用後, 就可以自己根據需求定製類似newFixedThreadPool
, newSingleThreadExecutor
, newCachedThreadPool
這樣的特定線程池.
參數名 | 描述 |
---|---|
corePoolSize | 核心線程數, 常駐線程, 除非設置了allowCoreThreadTimeOut |
maximumPoolSize | 最大線程數, maximumPoolSize - corePoolSize就是工作線程數 |
keepAliveTime | 工作線程idle 設定時間後, 會將閒置的工作線程回收, 如果設置了allowCoreThreadTimeOut, 核心線程也會被回收 |
TimeUnit | 線程回收需要等待閒置時間的單位 |
workQueue | 阻塞任務的排隊隊列 |
ThreadFactory | 線程池用來創建線程的工廠 |
handler | 線程池滿了等原因造成添加任務失敗的處理方案:AbortPolicy 默認策略直接拋異常.CallerRunsPolicy 在添加任務的線程裏運行當前任務.DiscardPolicy 放棄執行當前的任務.DiscardOldestPolicy 放棄工作隊列中排在頭部的一個任務, 在添加當前任務. |
運行狀態
狀態名 | 描述 | 下級狀態 |
---|---|---|
RUNNING | 正常運行, 可以提交新任務進來 | shutdown() -> SHUTDOWN. shutdownNown() -> STOP |
SHUTDOWN | 停止提交新任務, 正在運行和在隊列中排隊的任務繼續執行完成 | TIDYING |
STOP | 停止提交新任務, queu也阻塞, 並interrupt正在執行的線程 | TIDYING |
TIDYING | 調用停止後, 活動任務數量爲0時, 進入tidying狀態. | TERMINATED |
TERMINATED | tidying後會嘗試調用terminated方法進入 terminated狀態 | - |
工作隊列
緩存任務的排隊容器, 根據不同的長度和特性有不同的使用場景.
隊列名稱 | 是否有界 | 描述 |
---|---|---|
SynchronousQueue | 是 | 0容量無緩衝隊列, 有任務進來會直接給線程池去消費, 在消費的過程中會阻塞新任務進來. 當新任務不能進入消息隊列時, 線程池就會創建新的工作線程. 所以最好搭配maximumPoolSize的最大設置Integer.MAX_VALUE. 在執行任務時被線程池拒絕異常 |
ArrayBlockingQueue | 是 | 固定設置容量的數組隊列. 可以使用先入先出規則. |
LinkedBlockingQueue | 否 | 無限容量. 鏈表隊列. 先入先出. |
PriorityBlockingQueue | 否 | 同LinkedBlockingQueue, 任務必須實現compare方法, 來對鏈表進行排序 |
execute時線程數量和任務隊列的關係
ThreadPoolExecutor的執行流程如下圖所示, 簡單用三句話來描述:
- 核心線程數沒滿時, 就直接開核心線程.
- 核心線程數滿了, 來新任務後就扔到隊列中排隊.
- 如果隊列排滿了, 再去開工作線程. 直到線程數達到最大線程數.
無邊界隊列在使用時需要注意, 因爲一旦使用了無邊界隊列, 那麼線程池的maximumPoolSize就會失效. 除了corePoolSize數量的核心線程或者唯一一個工作線程在運行外, 其他所有的任務都會在工作隊列中排隊.
創建線程並消費隊列中任務
-
Worker
Worker是線程池中任務的消費者, 每一個Worker代表了一個線程. Worker本身是繼承於AbstractQueuedSynchronizer, 在構造時創建一個線程作爲自己的屬性, 通過設置status 0 | 1來修改當前的線程獨佔狀態. 如果有初始任務的話在啓動線程後直接運行, 並記錄自己完成任務的數量.
-
addWorker
線程池內部維護了一個workers數據集, 在內部訪問workers時必須用一個全局可重入鎖保證互斥. 創建worker的時機可以參考上一節的內容.
創建worker的細節如下圖所示, 簡單用三句話來描述:
- 如果當前線程池狀態不允許再創建worker, 則直接失敗.
- 創建worker後, 如果線程池和worker內部的線程狀態都正常, 將worker添加進workers數據集並更新數量.
- 如果worker已經添加到workers中後, 開始運行worker內部的線程.
-
runWroker
創建worker成功時, 會開始運行新worker內部的線程. 線程啓動後調用
runWorker(Worker)
方法, 在取任務時, 會根據不同的情況讓線程處於等待狀態.- 如果有初始任務或者能從阻塞隊列中提取到任務, 就執行
- 根據運行狀態重置或者設置線程的中斷位
- 任務執行前後回調線程池子類實現的方法
- 如果執行任務異常或者循環阻塞獲取任務爲null, 則標記並清理當前worker
-
getTask
在worker運行時會循環從阻塞隊列中獲取任務來執行, 這一過程會根據不同的情況阻塞等待不同的時間.
- 當前狀態不能獲取任務時, 直接返回空任務, 結束當前worker.
- 如果當前超時設置生效, 則從任務隊列中poll個任務出來. 如果隊列爲空則等待超時.
- 如果當前超時設置無效, 則從任務隊列中take個任務出來. 如果隊列爲空則一直等待, 直到線程中斷或接受到信號.
- 如果取任務時發生中斷或者任務爲空後從1開始循環.
- 返回有效的任務給worker運行.
轉載請註明出處:http://blog.csdn.net/l2show/article/details/103480185