面試官:核心線程數爲0時,線程池如何執行?

線程池是 Java 中用於提升程序執行效率的主要手段,也是併發編程中的核心實現技術,並且它也被廣泛的應用在日常項目的開發之中。那問題來了,如果把線程池中的核心線程數設置爲 0 時,線程池是如何執行的?

要回答這個問題,我們首先要了解在正常情況下,線程池的執行流程,也就是說當有一個任務來了之後,線程池是如何運行的?

1.線程池的執行流程

正常情況下(核心線程數不爲 0 的情況下)線程池的執行流程如下:

  1. 判斷核心線程數:先判斷當前工作線程數是否大於核心線程數,如果結果爲 false,則新建線程並執行任務。
  2. 判斷任務隊列:如果大於核心線程數,則判斷任務隊列是否已滿?如果結果爲 false,則把任務添加到任務隊列中等待線程執行。
  3. 判斷最大線程數:如果任務隊列已滿,則判斷當前線程數量是否超過最大線程數?如果結果爲 false,則新建線程執行此任務。
  4. 判斷是否要執行拒絕策略:如果超過最大線程數,則將執行線程池的拒絕策略。

如下圖所示:

核心線程數 VS 最大線程數

核心線程數(corePoolSize)和最大線程數(maximumPoolSize)都是線程池中的兩個重要參數,其中:

  1. 核心線程數定義了線程池中最小線程數量,即使這些線程處於空閒狀態,也不會被銷燬。
  2. 最大線程數定義了線程池中允許的最大線程數量,最大線程數等於核心線程數 + 臨時線程數,最大線程數主要是提供了一種機制來應對突發的高併發請求,當有大量任務的時候,可以創建線程數量的上線。

PS:在線程池的使用過程中,最大線程數必須大於等於核心線程數,否則程序執行會報錯。

2.核心線程爲0的執行流程

那麼問題來了,按照線程池的正常執行流程來看,如果核心線程數爲 0 的話,那麼當任務來了之後會判斷當前工作的線程數不大於核心線程數,那也就不會創建線程執行任務了,會將任務放到隊列。

但這個結果又很滑稽,有任務來了線程池竟然不執行,而是先放到任務隊列中,這好像有比較奇怪,這就好比你開了一個快遞店,當有快遞來了之後,你想的不是如何派送,而是直接把它丟到倉庫一樣滑稽,這會讓等快遞的人很着急,所以,我們需要驗證一下線程池的執行是否如我們猜想的那般,驗證代碼如下:

// 線程池核心線程數設置爲 0
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                0, 10, 1, TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10));
// 給線程池添加任務
threadPoolExecutor.execute(() -> {
    System.out.println("www.javacn.site");
});

最終程序的執行結果如下:
image.png
咦,結果怎麼不符合我們的預期呢?爲什麼任務來了之後,沒有將任務放到任務隊列而是直接執行了呢?

雖然程序執行結果符合正常邏輯,但又和我們瞭解的線程池執行理論相違背,這是什麼原因呢?

查看線程池的執行源碼

帶着這個疑問,我們查看了線程池的執行源碼發現,線程池的執行過程遠比我們想想的複雜,線程池核心源碼如下:
image.png
從上面源碼可以看出,當我們將任務添加到隊列的時候,線程池會判斷工作的線程數是否爲 0,也就是上面圈出來的那行代碼,如果當前工作線程爲 0 的話,會創建線程執行任務。哦,原來如此,這樣,就能將理論和實踐對應上了。

也就是說,當核心線程數爲 0 時,當來了一個任務之後,會先將任務添加到任務隊列,同時也會判斷當前工作的線程數是否爲 0,如果爲 0,則會創建線程來執行線程池的任務,這就是正確的線程池執行流程,同時也是面試官想要的答案。

課後思考

如何實現線程池的監控?如何動態調整線程池的核心線程數呢?

本文已收錄到我的面試小站 www.javacn.site,其中包含的內容有:Redis、JVM、併發、併發、MySQL、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、設計模式、消息隊列等模塊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章