摘要:與最大線程池比較。如果加入成功,需要二次檢查線程池的狀態如果線程池沒有處于,則從移除任務,啟動拒絕策略。如果線程池處于狀態,則檢查工作線程是否為。線程池將如何工作這個問題應該就不難回答了。
原文地址:https://www.xilidou.com/2018/02/09/thread-corepoolsize/
最近在看《Java并發編程的藝術》回顧線程池的原理和參數的時候發現一個問題,如果 corePoolSize = 0 且 阻塞隊列是無界的。線程池將如何工作?
我們先回顧一下書里面描述線程池execute()工作的邏輯:
如果當前運行的線程,少于corePoolSize,則創建一個新的線程來執行任務。
如果運行的線程等于或多于 corePoolSize,將任務加入 BlockingQueue。
如果 BlockingQueue 內的任務超過上限,則創建新的線程來處理任務。
如果創建的線程數是單錢運行的線程超出 maximumPoolSize,任務將被拒絕策略拒絕。
看了這四個步驟,其實描述上是有一個漏洞的。如果核心線程數是0,阻塞隊列也是無界的,會怎樣?如果按照上文的邏輯,應該沒有線程會被運行,然后線程無限的增加到隊列里面。然后呢?
于是我做了一下試驗看看到底會怎樣?
public class threadTest { private final static ThreadPoolExecutor executor = new ThreadPoolExecutor(0,1,0, TimeUnit.MILLISECONDS,new LinkedBlockingQueue()); public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(); while (true) { executor.execute(() -> { System.out.println(atomicInteger.getAndAdd(1)); }); } } }
結果里面的System.out.println(atomicInteger.getAndAdd(1));語句執行了,與上面的描述矛盾了。到底發生了什么?線程池創建線程的邏輯是什么?我們還是從源碼來看看到底線程池的邏輯是什么?
ctl要了解線程池,我們首先要了解的線程池里面的狀態控制的參數 ctl。
線程池的ctl是一個原子的 AtomicInteger。
這個ctl包含兩個參數 :
workerCount 激活的線程數
runState 當前線程池的狀態
它的低29位用于存放當前的線程數, 因此一個線程池在理論上最大的線程數是 536870911; 高 3 位是用于表示當前線程池的狀態, 其中高三位的值和狀態對應如下:
111: RUNNING
000: SHUTDOWN
001: STOP
010: TIDYING
110: TERMINATED
為了能夠使用 ctl 線程池提供了三個方法:
// Packing and unpacking ctl // 獲取線程池的狀態 private static int runStateOf(int c) { return c & ~CAPACITY; } // 獲取線程池的工作線程數 private static int workerCountOf(int c) { return c & CAPACITY; } // 根據工作線程數和線程池狀態獲取 ctl private static int ctlOf(int rs, int wc) { return rs | wc; }execute
外界通過 execute 這個方法來向線程池提交任務。
先看代碼:
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); int c = ctl.get(); //如果工作線程數小于核心線程數, if (workerCountOf(c) < corePoolSize) { //執行addWork,提交為核心線程,提交成功return。提交失敗重新獲取ctl if (addWorker(command, true)) return; c = ctl.get(); } //如果工作線程數大于核心線程數,則檢查線程池狀態是否是正在運行,且將新線程向阻塞隊列提交。 if (isRunning(c) && workQueue.offer(command)) { //recheck 需要再次檢查,主要目的是判斷加入到阻塞隊里中的線程是否可以被執行 int recheck = ctl.get(); //如果線程池狀態不為running,將任務從阻塞隊列里面移除,啟用拒絕策略 if (! isRunning(recheck) && remove(command)) reject(command); // 如果線程池的工作線程為零,則調用addWoker提交任務 else if (workerCountOf(recheck) == 0) addWorker(null, false); } //添加非核心線程失敗,拒絕 else if (!addWorker(command, false)) reject(command); }addWorker
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); //獲取線程池狀態 int rs = runStateOf(c); // Check if queue empty only if necessary. // 判斷是否可以添加任務。 if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { //獲取工作線程數量 int wc = workerCountOf(c); //是否大于線程池上限,是否大于核心線程數,或者最大線程數 if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; //CAS 增加工作線程數 if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl //如果線程池狀態改變,回到開始重新來 if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } } boolean workerStarted = false; boolean workerAdded = false; Worker w = null; //上面的邏輯是考慮是否能夠添加線程,如果可以就cas的增加工作線程數量 //下面正式啟動線程 try { //新建worker w = new Worker(firstTask); //獲取當前線程 final Thread t = w.thread; if (t != null) { //獲取可重入鎖 final ReentrantLock mainLock = this.mainLock; //鎖住 mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); // rs < SHUTDOWN ==> 線程處于RUNNING狀態 // 或者線程處于SHUTDOWN狀態,且firstTask == null(可能是workQueue中仍有未執行完成的任務,創建沒有初始任務的worker線程執行) if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { // 當前線程已經啟動,拋出異常 if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); //workers 是一個 HashSet 必須在 lock的情況下操作。 workers.add(w); int s = workers.size(); //設置 largeestPoolSize 標記workAdded if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } //如果添加成功,啟動線程 if (workerAdded) { t.start(); workerStarted = true; } } } finally { //啟動線程失敗,回滾。 if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
先看看 addWork() 的兩個參數,第一個是需要提交的線程 Runnable firstTask,第二個參數是 boolean 類型,表示是否為核心線程。
execute() 中有三處調用了 addWork() 我們逐一分析。
第一次,條件 if (workerCountOf(c) < corePoolSize) 這個很好理解,工作線程數少于核心線程數,提交任務。所以 addWorker(command, true)。
第二次,如果 workerCountOf(recheck) == 0 如果worker的數量為0,那就 addWorker(null,false)。為什么這里是 null ?之前已經把 command 提交到阻塞隊列了 workQueue.offer(command) 。所以提交一個空線程,直接從阻塞隊列里面取就可以了。
第三次,如果線程池沒有 RUNNING 或者 offer 阻塞隊列失敗,addWorker(command,false),很好理解,對應的就是,阻塞隊列滿了,將任務提交到,非核心線程池。與最大線程池比較。
至此,重新歸納execute()的邏輯應該是:
如果當前運行的線程,少于corePoolSize,則創建一個新的線程來執行任務。
如果運行的線程等于或多于 corePoolSize,將任務加入 BlockingQueue。
如果加入 BlockingQueue 成功,需要二次檢查線程池的狀態如果線程池沒有處于 Running,則從 BlockingQueue 移除任務,啟動拒絕策略。
如果線程池處于 Running狀態,則檢查工作線程(worker)是否為0。如果為0,則創建新的線程來處理任務。如果啟動線程數大于maximumPoolSize,任務將被拒絕策略拒絕。
如果加入 BlockingQueue 。失敗,則創建新的線程來處理任務。
如果啟動線程數大于maximumPoolSize,任務將被拒絕策略拒絕。
總結回顧我開始提出的問題:
如果 corePoolSize = 0 且 阻塞隊列是無界的。線程池將如何工作?
這個問題應該就不難回答了。
最后《Java并發編程的藝術》是一本學習 java 并發編程的好書,在這里推薦給大家。
同時,希望大家在閱讀技術數據的時候要仔細思考,結合源碼,發現,提出問題,解決問題。這樣的學習才能高效且透徹。
歡迎關注我的微信公眾號
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/68556.html
摘要:異步任務的構造方法主要用于初始化線程池先關的成員變量創建一個新的異步任務。所以,我們是必須確保在銷毀活動之前取消任務。 目錄介紹 01.先看下AsyncTask用法 02.AsyncTask源碼深入分析 2.1 構造方法源碼分析 2.2 看execute(Params... params)方法 2.3 mWorker和mFuture的創建過程 03.異步機制的實現 04.不同...
摘要:使用線程池的好處通過線程在自己的線程池中隔離的好處是該應用程序完全可以不受失控的客戶端庫的威脅。簡而言之,由線程池提供的隔離功能可以使客戶端庫和子系統性能特性的不斷變化和動態組合得到優雅的處理,而不會造成中斷。 ? 工作流程圖 下面的流程圖展示了當使用Hystrix的依賴請求,Hystrix是如何工作的。showImg(https://segmentfault.com/img/bV0...
摘要:提高線程的可管理性線程池可以統一管理分配調優和監控。線程池的初始化狀態是。調用線程池的接口時,線程池由。當所有的任務已終止,記錄的任務數量為,阻塞隊列為空,線程池會變為狀態。線程池徹底終止,就變成狀態。 序言 我們知道,線程池幫我們重復管理線程,避免創建大量的線程增加開銷。合理的使用線程池能夠帶來3個很明顯的好處:1.降低資源消耗:通過重用已經創建的線程來降低線程創建和銷毀的消耗2.提...
摘要:介紹線程池一般包含三個主要部分調度器決定由哪個線程來執行任務執行任務所能夠的最大耗時等線程隊列存放并管理著一系列線程這些線程都處于阻塞狀態或休眠狀態任務隊列存放著用戶提交的需要被執行的任務一般任務的執行的即先提交的任務先被執行調度器并非是必 介紹 線程池一般包含三個主要部分: 調度器: 決定由哪個線程來執行任務, 執行任務所能夠的最大耗時等 線程隊列: 存放并管理著一系列線程, 這些...
閱讀 1370·2021-11-25 09:43
閱讀 3582·2021-11-10 11:48
閱讀 5091·2021-09-23 11:21
閱讀 1597·2019-08-30 15:55
閱讀 3508·2019-08-30 13:53
閱讀 1235·2019-08-30 10:51
閱讀 868·2019-08-29 14:20
閱讀 1972·2019-08-29 13:11