這個坑不是我挖的,我是無意間看到已離職同事的代碼,這個代碼是兩年前寫的了,聲明的線程池方式如下:
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);
}
希望大家引以爲戒。