今天同事突然提出問題說用哪個線程池好,newFixedThreadPool和newCacheThreadPool里選擇,說固定大小線程池keepAliveTime=0,線程空閑會立馬回收線程從而節約資源,然后另外一個同事說,0是代表永遠不回收,我記憶里也是記得0是永久存活,因為網上很多博客啊,資料啊都是說的0表示線程永久存活在空閑的時候。前面那位同事也是從字面上認為的,沒有經過驗證,覺得-1才是永久不回收,然后各自進行了一波研究分析。
經過看源碼,發現keepAliveTime<0是不行的,直接報錯,也就是同事的猜測-1才是不回收 是錯誤的,看下面代碼圖示(別問我怎么給代碼里部分標紅,直接用瀏覽器F12自己編輯頁面寫標簽樣式進去的。。)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
然后找keepAliveTime的使用一時半會也沒找到,就用代碼測試了,發現設置keepAliveTime=0核心線程數確實沒有回收,后面同事說在並發病程的藝術那本書上有一段描述
“當線程池中的線程數大於corePoolSize時,keepAliveTime為多余的空閑線程等待新任務的 最長時間,超過這個時間后多余的線程將被終止。這里把keepAliveTime設置為0L,意味着多余 的空閑線程會被立即終止。”
同事又開始迷茫了,我一開始沒細看,覺得說的不對,后面反復閱讀,發現這段文字描述的是說keepAliveTime控制的是非核心線程數的回收,也就是0的時候,非核心線程數會在空閑的時候回收,並不是說核心的會回收。
為了驗證結果,我們就用代碼進行了測試,測試代碼如下:
package com.xhs.concurrent.threaddemo.sync;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author xuhan build 2019/4/23
*/
public class ExecutorsDemo implements Runnable{
private int i=0;
public ExecutorsDemo(int i) {
this.i = i;
}
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,2,0, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1));
for(int i=0;i<3;i++){
executor.execute(new ExecutorsDemo(i));
}
while(true){
System.out.println("總線程數:"+executor.getPoolSize()+"當前活躍線程數:"+executor.getActiveCount());
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void run() {
System.out.println("i="+i+" Thread = "+Thread.currentThread().getName());
if(i>=1){
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("i="+i+" sleep 1 s結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("i="+i+" sleep 3 s結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
設置核心線程數和非核心線程數分別為1個,隊列容量為1,進入3個runnable:
第一個創建主線程,第二個進入隊列,第三個則創建非主線程運行,
輸出結果為
i=0 Thread = pool-1-thread-1
i=2 Thread = pool-1-thread-2
總線程數:2當前活躍線程數:2
總線程數:2當前活躍線程數:2
i=2 sleep 1 s結束
i=1 Thread = pool-1-thread-2
總線程數:2當前活躍線程數:2
總線程數:2當前活躍線程數:2
i=1 sleep 1 s結束
總線程數:1當前活躍線程數:1
總線程數:1當前活躍線程數:1
i=0 sleep 3 s結束
總線程數:1當前活躍線程數:0
可以看到非核心數線程執行完畢之后,隊列中的task進入繼續執行,等再次進入隊列的task結束后,可以看到總線程數減少了1,而等核心線程執行完畢后,發現總線程數沒有減少,但活躍線程數減少,也就是核心線程數沒有回收。書上說的是正確的,大部分網上的博客說的keepAliveTime=0永久不回收是有出入的。
如果要設置核心線程的回收,則需要設置
executor.allowCoreThreadTimeOut(true);
但這是keepAliveTime必須要>0才行,否則會拋出異常!!!
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
如果看了之后覺得有問題,希望各位指點一番!!謝謝!!