微信公衆號「後端進階」,專一後端技術分享:Java、Golang、WEB框架、分佈式中間件、服務治理等等。
老司機傾囊相授,帶你一路進階,來不及解釋了快上車!
看完我上一篇文章「你都理解建立線程池的參數嗎?」以後,當遇到這種問題,你以爲你徹底可以唬住面試官了,50k輕鬆到手。卻不知,要是面試官此刻給你來個反殺:java
初始化線程池時能夠預先建立線程嗎?線程池的核心線程能夠被回收嗎?爲何?git
若是此刻你一臉懵逼,這個要慌,問題很大,50k立刻變5k。github
有細心的網友早就想到了這個問題:面試
在ThreadPoolExecutor線程池中,還有一些不經常使用的設置。我建議若是您在應用場景中沒有特殊的要求,就不須要使用這些設置。後端
初始化線程池時是能夠預先建立線程的,初始化線程池後,再調用prestartAllCoreThreads()方法,便可預先建立corePoolSize數量的核心線程,咱們看源碼:微信
public int prestartAllCoreThreads() { int n = 0; while (addWorker(null, true)) ++n; return n; }
private boolean addWorker(Runnable firstTask, boolean core) { // .. }
addWorker方法目的是在線程池中添加任務並執行,若是task爲空,線程獲取任務執行時調用getTask()方法,該方法從blockingQueue阻塞隊列中阻塞獲取任務執行,所以線程不會釋放,留存在線程池中,若是core=true,說明任務只能利用核心線程來執行。框架
因此該方法會在線程池總預先建立沒有任務執行的線程,數量爲corePoolSize。分佈式
下面咱們測試一下:測試
從測試結果來看,線程池中已經預先建立了corePoolSize數量的空閒線程。spa
prestartCoreThread()一樣能夠預先建立線程,只不過該方法只會與建立1條線程,咱們來看源碼:
public boolean prestartCoreThread() { return workerCountOf(ctl.get()) < corePoolSize && addWorker(null, true); }
從方法源碼可知,若是此時工做線程數量小於corePoolSize,那麼就調用addWorker建立1條空閒核心線程。
下面咱們測試一下:
從測試結果來看,線程池中已經預先建立了1條空閒線程。
你可能會想到將corePoolSize的數量設置爲0,從而線程池的全部線程都是「臨時」的,只有keepAliveTime存活時間,你的思路也許時正確的,但你有沒有想過一個很嚴重的後果,corePoolSize=0時,任務須要填滿阻塞隊列纔會建立線程來執行任務,阻塞隊列有設置長度還好,若是隊列長度無限大呢,你就等着OOM異常吧,因此用這種設置行爲並非咱們所須要的。
有沒有什麼設置能夠回收核心線程呢?
ThreadPoolExecutor有一個私有成員變量:
private volatile boolean allowCoreThreadTimeOut;
若是allowCoreThreadTimeOut=true,核心線程在規定時間內會被回收。
上面我也說了,當線程空閒時會從blockingQueue阻塞隊列中阻塞獲取任務執行,因此咱們來看看是保證核心線程不被銷燬的,咱們直接定位到源碼部位:
java.util.concurrent.ThreadPoolExecutor#getTask:
boolean timedOut = false; // Did the last poll() time out? for (;;) { // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } }
這裏的關鍵值timed,若是allowCoreThreadTimeOut=true或者此時工做線程大於corePoolSize,timed=true,若是timed=true,會調用poll()方法從阻塞隊列中獲取任務,不然調用take()方法獲取任務。
下面我來解釋這兩個方法:
到這裏,咱們就很好地解釋了,當allowCoreThreadTimeOut=true或者此時工做線程大於corePoolSize時,線程調用BlockingQueue的poll方法獲取任務,若超過keepAliveTime時間,則返回null,timedOut=true,則getTask會返回null,線程中的runWorker方法會退出while循環,線程接下來會被回收。
下面咱們測試一下:
能夠看到,核心線程被回收了。