首先看一下線程被銷燬的代碼:
// 當線程獲取不到tasks, 就調用processWorkerExit方法, 處理線程退出
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w); // 將線程從HashSet中移除
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
照理說不管是核心線程還是非核心線程,blockingQueue不可能一直都有task,總有處理完的時候,一旦blockingQueue中沒有task可處理,那麼這個線程就一定會被remove,因爲processWorkerExit方法是寫在finally裏面的;
// 線程池worker處理task方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
那麼,如何保證核心線程不退出呢?
再來明確一個概念,所謂的核心線程與非核心線程,在線程池的數據結構中並沒有明確區分,只要線程池剩餘的線程數小於等於corePoolSize,那麼剩下的線程都可以稱爲核心線程;
再有,一個核心線程要想不被銷燬,那麼只有一種可能,那就是線程等待,等待有新的task到來;
換句話說:一旦線程池的線程個數小於等於corePoolSize,task = getTask() 是永遠不會返回null的(非異常情況)。
我們來看看getTask方法是怎麼處理的:
// 通過上面分析,可以得出結論:
// 1,線程數大於corePoolSize,當隊列無task或者線程池狀態異常時return null
// 2,線程數小於等於corePoolSize,getTask不會返回null,異常情況除外
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) { // 死循環
int c = ctl.get();
int rs = runStateOf(c); // 取線程池狀態
// Check if queue empty only if necessary.
// 這個if判斷也很重要,如果調用了shutdown/shutdownNow方法,那麼“核心線程”也會返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c); // 取線程數
// Are workers subject to culling?
// allowCoreThreadTimeOut = false
// timed = true 表示存在非核心線程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// wc > maximumPoolSize = false 一般不會出現這種情況,所以是false
// 這個if條件成立的前提:必須存在非核心線程【關鍵】
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null; // 這個null就是非核心線程被銷燬的罪魁禍首
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r; // 能取到任務就返回,否則設置timedOut進去死循環
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
——綜上,我們總結了Java線程池的一個小細節!