ThreadPoolExecutor使用LinkedBlockingQueue的坑

原文鏈接:https://www.jianshu.com/p/78c7df3c762d
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
    4, // corePoolSize
    64, // maximumPoolSize
    60L, // keepAliveTime
    TimeUnit.SECONDS, // timeUnit
    new LinkedBlockingQueue<>(100000) // workQueue
);

我們有時候會使用上述的代碼來新建一個ThreadPoolExecutor對象,使用LinkedBlockingQueue作爲workQueue。

需要特別注意的是,上述創建的ThreadPoolExecutor對象,當同時有64個任務進來時,並不會創建64個線程來同時處理這64個任務,而只創建4個core線程優先處理最先進來的4個任務,將後進來的60個任務放入LinkedBlockingQueue隊列中,隊列中的任務排隊等待被這4個core線程處理。

使用LinkedBlockingQueue作爲workQueue,ThreadPoolExecutor對象在隊列不滿的情況下只會創建core線程,不會創建非core線程,設置的maximumPoolSize並不起作用。

原因分析

下述代碼是ThreadPoolExecutor的execute方法,其中描述了新建線程的機制。可以發現只有當滿足下面的兩個條件之一時,纔會新建線程:

  1. 當前存活的線程數小於corePoolSize;(新建core線程)
  2. 往workQueue添加元素失敗;(新建非core線程)
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) { // 條件1:存活線程小於corePoolSize,新建core線程
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0) // 正常情況下,這裏的條件不會滿足
                addWorker(null, false);
        }
        else if (!addWorker(command, false)) // 條件2:往隊列添加元素失敗,新建非core線程
            reject(command);
    }

可以發現能新建非core線程的直接原因是隊列workQueue添加元素失敗,因此選擇不同的BlockingQueue實現類會對新建線程產生很大的影響,常用的BlockingQueue:

  • LinkedBlockingQueue:隊列已滿時會添加失敗;
  • SynchronousQueue:如果沒有其他線程在等待獲取元素時會添加失敗;

 

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