Java執行緒池maximumPoolSize和workQueue哪個先飽和?

救苦救难韩天尊發表於2024-06-13

先說結論,真正的飽和順序是corePoolSize -> workQueue -> maximumPoolSize。


我們都知道,執行緒池有以下引數

ThreadPoolExecutor(int corePoolSize, //核心執行緒數
                  int maximumPoolSize, //
                  long keepAliveTime,
                  TimeUnit unit,
                  BlockingQueue<Runnable> workQueue)
  • corePoolSize:核心執行緒池大小,即使沒有任務,這些執行緒也會一直存活。
  • maximumPoolSize:最大執行緒池大小,當任務量超過核心執行緒數時,最多可以建立這麼多執行緒。
  • keepAliveTime:非核心執行緒在空閒時等待新任務的最長時間。
  • unit:keepAliveTime 的時間單位。
  • workQueue:阻塞佇列,用於儲存待執行的任務。常見的阻塞佇列實現有 ArrayBlockingQueue、LinkedBlockingQueue 和 SynchronousQueue 等。

我之前的理解是,當執行緒數達到corePoolSize後,新任務到來會建立新執行緒直到達到maximumPoolSize,當達到maximumPoolSize後,新任務才會放到workQueue裡。

但其實,這一直都是錯誤的理解,真正的飽和順序是corePoolSize->workQueue->maximumPoolSize。

下面開始驗證

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
for (int i = 0; i < 6; i++) {
    int finalI = i;
    executor.submit(() -> {
        System.out.println(Thread.currentThread().getName() + " " + finalI);
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
}
executor.shutdown();

若corePoolSize滿了後新任務是建立新執行緒,那麼這裡應該一次列印6條日誌。

若corePoolSize滿了之後新任務放到workQueue裡,那麼這裡應該列印兩次,第一次5條,第二次1條。

列印結果

由此可見符合第二種情況,為了證明可信度下面再驗證一種情況

ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
for (int i = 0; i < 16; i++) {
    int finalI = i;
    executor.submit(() -> {
        System.out.println(Thread.currentThread().getName() + " " + finalI);
        try {
            Thread.sleep(5000L);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    });
}
executor.shutdown();

按照上面的結論推測,當我們有16個要執行的任務,首先會佔用5個核心執行緒,然後放10個任務到阻塞佇列,剩下的1個加上核心執行緒數是6個,達不到最大執行緒數,所以會建立活動執行緒,此時執行緒池中會有6個可用執行緒。所以會分三次列印結果,第一次6條,第二次6條,第三次4條。下面驗證推測

驗證成功!

相關文章