ThreadPoolExecutor中的keepAliveTime詳解
閱讀這篇文章,你將會知道:
- keepAliveTime的概念。
- keepAliveTime是如何設置的。
- 線程是如何根據keepAliveTime進行銷燬的。
一.keepAliveTime的概念:
- keepAliveTime的單位是納秒,即1s=1000000000ns,1秒等於10億納秒。
- keepAliveTime是線程池中空閒線程等待工作的超時時間。
- 當線程池中線程數量大於corePoolSize(核心線程數量)或設置了allowCoreThreadTimeOut(是否允許空閒核心線程超時)時,線程會根據keepAliveTime的值進行活性檢查,一旦超時便銷燬線程。
- 否則,線程會永遠等待新的工作。
/**
* Timeout in nanoseconds for idle threads waiting for work.
* Threads use this timeout when there are more than corePoolSize
* present or if allowCoreThreadTimeOut. Otherwise they wait
* forever for new work.
*/
private volatile long keepAliveTime;
二. keepAliveTime的設置方法
1.通過構造函數設置
通過 keepAliveTime 、unit共同決定實際的 keepAliveTime值,最終會轉化成納秒單位。
2.通過setKeepAliveTime方法動態設置
重新設置線程池的keepAliveTime屬性,如果發現將要設置的值比原來的keepAliveTime值要小(即減小keepAliveTime),則觸發interruptIdleWorkers(),中斷空閒線程。
interruptIdleWorkers()是怎麼中斷線程的呢?
(1)interruptIdleWorkers先拿出所有的工作者進行遍歷,判斷工作者對應的線程是否已經中斷。
(2)如果沒有產生中斷,則判斷是否可以獲得鎖,如果能獲得鎖,則代表是空閒線程,然後中斷該線程。
(3)至於線程的中斷在什麼時候會拋出中斷異常,同學們可以自己找下資料,也可以參考下別人寫的這篇文章Java併發之線程中斷
三.線程是如何根據keepAliveTime進行銷燬的
-
線程池中的線程通過工作者(Worker)這個類進行包裝,Worker通過 ThreadPoolExecutor.runWorker() 這個方法進行自旋,從隊列中獲得task,並完成工作。
-
如果拿不到task(即firstTask == null 或 getTask() == null),則會退出自旋,進入finally代碼塊。finally中會調用processWorkerExit方法,註銷當前Worker,實現worker的銷燬。對keepAliveTime的使用,就在getTask()方法中,這個在後面講解。
-
getTask 怎麼使用 keepAliveTime
(1)首先也是一個自旋,當allowCoreThreadTimeout(運行空閒核心線程超時) 或 wc>corePoolSize(當前線程數量大於核心線程數量) 時,timed會標識爲true,表示需要進行超時判斷。
(2)當wc(當前工作者數量)大於 最大線程數 或 空閒線程的空閒時間大於keepAliveTime(timed && timeout),以及wc>1或(workQueue)任務隊列爲空時,會進入compareAndDecrementWorkerCount方法,對wc的值減1。
(3)當compareAndDecrementWorkerCount方法返回true時,則getTask方法會返回null,終止getTask方法的自旋。這時候回到runWorker方法,就會進入到processWorkerExit方法,進行銷燬worker。
-
compareAndDecrementWorkerCount中操作的是ctl屬性:
(1)ctl是中心控制器,一個AtomicInteger類型的整數,通過數字的二進制編碼的位進行分段,不同的二進制位段表示有不同的含義。
(2)在ctl中,低29爲表示線程池的容量,即線程池最大容量爲 536870911 = 000 11111111111111111111111111111。
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*/
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// COUNT_BITS = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// CAPACITY = 536870911 = 000 11111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
private static int workerCountOf(int c) {
return c & CAPACITY;
}