執行緒池是 Java 中用於提升程式執行效率的主要手段,也是併發程式設計中的核心實現技術,並且它也被廣泛的應用在日常專案的開發之中。那問題來了,如果把執行緒池中的核心執行緒數設定為 0 時,執行緒池是如何執行的?
要回答這個問題,我們首先要了解在正常情況下,執行緒池的執行流程,也就是說當有一個任務來了之後,執行緒池是如何執行的?
# 1.執行緒池的執行流程
正常情況下(核心執行緒數不為 0 的情況下)執行緒池的執行流程如下:
判斷核心執行緒數:先判斷當前工作執行緒數是否大於核心執行緒數,如果結果為 false,則新建執行緒並執行任務。
判斷任務佇列:如果大於核心執行緒數,則判斷任務佇列是否已滿?如果結果為 false,則把任務新增到任務佇列中等待執行緒執行。
判斷最大執行緒數:如果任務佇列已滿,則判斷當前執行緒數量是否超過最大執行緒數?如果結果為 false,則新建執行緒執行此任務。
判斷是否要執行拒絕策略:如果超過最大執行緒數,則將執行執行緒池的拒絕策略。
如下圖所示:
# 核心執行緒數 VS 最大執行緒數
核心執行緒數(corePoolSize)和最大執行緒數(maximumPoolSize)都是執行緒池中的兩個重要引數,其中:
核心執行緒數定義了執行緒池中最小執行緒數量,即使這些執行緒處於空閒狀態,也不會被銷燬。
最大執行緒數定義了執行緒池中允許的最大執行緒數量,最大執行緒數等於核心執行緒數 + 臨時執行緒數,最大執行緒數主要是提供了一種機制來應對突發的高併發請求,當有大量任務的時候,可以建立執行緒數量的上線。
PS:線上程池的使用過程中,最大執行緒數必須大於等於核心執行緒數,否則程式執行會報錯。
# 2.核心執行緒為0的執行流程
那麼問題來了,按照執行緒池的正常執行流程來看,如果核心執行緒數為 0 的話,那麼當任務來了之後會判斷當前工作的執行緒數不大於核心執行緒數,那也就不會建立執行緒執行任務了,會將任務放到佇列。
但這個結果又很滑稽,有任務來了執行緒池竟然不執行,而是先放到任務佇列中,這好像有比較奇怪,這就好比你開了一個快遞店,當有快遞來了之後,你想的不是如何派送,而是直接把它丟到倉庫一樣滑稽,這會讓等快遞的人很著急,所以,我們需要驗證一下執行緒池的執行是否如我們猜想的那般,驗證程式碼如下:
// 執行緒池核心執行緒數設定為 0ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( 0, 10, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));// 給執行緒池新增任務threadPoolExecutor.execute(() -> { System.out.println("www.javacn.site");});
最終程式的執行結果如下:
咦,結果怎麼不符合我們的預期呢?為什麼任務來了之後,沒有將任務放到任務佇列而是直接執行了呢?
雖然程式執行結果符合正常邏輯,但又和我們瞭解的執行緒池執行理論相違背,這是什麼原因呢?
# 檢視執行緒池的執行原始碼
帶著這個疑問,我們檢視了執行緒池的執行原始碼發現,執行緒池的執行過程遠比我們想想的複雜,執行緒池核心原始碼如下:
從上面原始碼可以看出,當我們將任務新增到佇列的時候,執行緒池會判斷工作的執行緒數是否為 0,也就是上面圈出來的那行程式碼,如果當前工作執行緒為 0 的話,會建立執行緒執行任務。哦,原來如此,這樣,就能將理論和實踐對應上了。
也就是說,當核心執行緒數為 0 時,當來了一個任務之後,會先將任務新增到任務佇列,同時也會判斷當前工作的執行緒數是否為 0,如果為 0,則會建立執行緒來執行執行緒池的任務,這就是正確的執行緒池執行流程。