我們知道線程池在兩種情況下會添加線程1.線程數量小於coresize時,添加任務會添加線程 2.線程數量大於coresize 小於maximumPoolSize時,且任務隊列滿了 會添加線程。 那么線程池中的線程銷毀與存活條件是怎樣的呢?
目前java的線程池中關於線程的數量主要有兩個值值得注意。corePoolSize和maximumPoolSize。前者代表java核心線程數量,后者代表的是最大線程數量,可以通過構造函數設定。而其中線程的存活條件我們可以通過其處理任務隊列的邏輯來分析
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock(); ..//代碼略 } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
這個方法是線程池中的每個工作線程最終會執行的邏輯,此時該線程已經在線程池中。 每個工作線程都會執行getTask()方法從任務隊列中獲取任務然后執行,如果獲取到位空的值,那該線程也就會跳出循環,不在處理任務隊列中的任務,被當做一個廢棄線程處理。那線程的存活條件我們看來可以從getTask方法中找到。
private Runnable getTask() { //超時標志設置默認設置為false 如果從任務隊列中獲取到null的話就會為true boolean timedOut = false; for (;;) { //ctl為元數據 保存了兩部分數據 一部分是工作線程數量 另一部分是當前線程的狀態 int c = ctl.get(); //獲取當前線程池的狀態 int rs = runStateOf(c); // 特殊情況 如果線程池停掉了 那么就減少線程數量並返回null 當前線程廢棄 if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } //獲取當前線程池的工作線程數量 int wc = workerCountOf(c); // allowCoreThreadTimeOut 默認為false 主要用來控制coresize線程的超時 //允許超時標志 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //如果工作線程數量大於了maximumPoolSize或者(允許超時且已經超時) 且線程數量大於1或者或者任務隊列已經空了 則允許減少 if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { //允許超時使用poll 不允許超時使用take ,take會一直阻塞直到有值進入 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; //到這兒說明允許超時並且確實超時了,那就將超時設置為true 如果下次還滿足超時條件就會將該線程作為廢棄線程 timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
其實這兒可以分為兩條線進來就容易理解。我們把當前的工作線程數量記為wc (work count)。這兒記住如果線程被停掉了那會直接廢棄該線程。下面討論的是線程正常運作時候的狀態。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; //如果工作線程數量大於了maximumPoolSize或者(允許超時且已經超時) 且線程數量大於1或者或者任務隊列已經空了 則允許減少 if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; }
上述代碼就是核心的判斷邏輯決定該線程在線程池正常狀態下是否要被廢棄。 根據該情況可以看到要滿足當前線程被廢棄,有個大前提條件,那就是此時線程數量大於1,或者線程數量等於1時任務隊列為空。
滿足上面的大前提的條件下再分析下具體的不同情況
第一種情況是wc<=coresize。此時該線程是否要被廢棄則取決於我們在構建線程時allowCoreThreadTimeOut的值設置,默認為false 。該值的不同會有如下兩種情況
- 如果為false,那么其從任務隊列獲取任務時會調用take方法,該方法會阻塞到一直有值返回。所有該線程永遠不會被廢棄
- 如果為true,那么注意會經歷兩次循環,第一次循環由於超時標志timedOut默認 為false所以不會執行上面的判斷代碼。會執行下方的poll附帶超時方法,此時如果返回為null則timedOut會被設置為true表明該線程已經超時,就會進入第二次循環,此時超時標簽timeout會為true,此時如果滿足上面的大前提條件則該線程會被廢棄
第二種情況是wc>coresize。此時該線程是否要被廢棄則主要取決於wc是否大於maximumPoolSize 也就是我們設置的最大值。該結果的不同也會有兩種情況
- 如果大於了maximumPoolSize ,且滿足上面的大前提條件,則該線程會被廢棄
- 如果不大於maximumPoolSize。那么也要注意會經歷兩次循環,第一次循環由於超時標志timedOut默認為false,所以不會執行上面的判斷代碼。會執行下方的poll附帶超時方法,此時如果返回為null則timedOut會被設置為true表明該線程已經超時,就會進入第二次循環,此時超時標簽timeout會為true,如果此時滿足上面的大前提條件,則該線程會被廢棄
結論
簡單概括一下上面的結論就是:線程已經被停掉了則當前線程會被廢棄。如果沒停掉的話如果wc<=coresize,且allowCoreThreadTimeOut=false則當前線程不會被廢棄。
否則在滿足大前提的條件下,wc>maximumPoolSized的時候會被廢棄 或者 (wc<=coresize且allowCoreThreadTimeOut=true)或者(coresize<wc<=maximumPoolSize),而線程在超時時間內沒有獲取到任務,就會被廢棄,