線程池ThreadPoolExecutor使用踩坑

這個坑不是我挖的,我是無意間看到已離職同事的代碼,這個代碼是兩年前寫的了,聲明的線程池方式如下:

private static final ThreadPoolExecutor threadPool = new ThreadPoolExecutor(0,
			100, 60L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

如果你能一眼看出問題,那就不需要往下看了。

坑就出在這個五個參數上,依次如下:
corePoolSize(核心線程池): 0
maximumPoolSize(最大線程池): 100
keepAliveTime(空閒線程保留時間): 60
timeUnit(時間單位): 秒
workQueue(阻塞隊列): LinkedBlockingQueue

雖然上面的corePoolSize設置爲0,但是至少會有一個線程,所以在實際運行時線程池中會有一個線程
按上面這個方式初始化的線程池,線程池裏面永遠只會有一個線程在處理。
因爲當線程數量達到corePoolSize後,阻塞隊列被佔滿後纔會繼續創建線程(線程數<maximumPoolSize),上面阻塞隊列採用的是無界對列LinkedBlockingQueue,不可能佔滿,所以maximumPoolSize參數也相當於無效了。

測試程序:

public class ThreadPoolTest {
	private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(3, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
	public static void main(String[] args) throws InterruptedException {
		for(int i=0;i<30;i++) {
			Thread t = new Thread(()-> {
				try {
					TimeUnit.SECONDS.sleep(5);
					System.out.println("threadName:"+Thread.currentThread().getName());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}); 
			poolExecutor.execute(t);
		}
	}
}

永遠只會有三個工作線程,輸出結果:

threadName:pool-1-thread-1
threadName:pool-1-thread-2
threadName:pool-1-thread-3
threadName:pool-1-thread-3
threadName:pool-1-thread-2
threadName:pool-1-thread-1
threadName:pool-1-thread-3
threadName:pool-1-thread-1

當把阻塞隊列的大小設置爲20,任務把阻塞隊列佔滿,然後增加工作線程,工作線程才能達到10個。

下面是ThreadPoolExecutor.execute()方法源碼:

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
     
        int c = ctl.get();
        // 小於核心線程池時直接添加線程
        if (workerCountOf(c) < corePoolSize) {
            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);
            // 再次 判斷當前線程數量是否等於0,等於0就新增加任務(一般都不滿足)
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

希望大家引以爲戒。

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