目錄
介紹
ThreadPoolExecutor 是是一種 ExecutorService(ExecutorService 的實現),內部維護了一個線程池,並利用線程池中的線程執行提交的任務。Executors 提供了許多創建固定配置的 ThreadPoolExecutor,用於快速提供 ThreadPoolExecutor。
ThreadPoolExecutor 提供線程池執行任務的目的是爲了解決
- 減少了爲每個任務創建單獨線程的開銷,提升了執行大量任務的性能。
- 提供用於管理線程、任務執行等一些列方法
- 提供了基本的統計方法,比如完成了多少個任務等。
ThreadPoolExecutor 提供了許多可調節執行異步任務策略的參數和一些擴展鉤子。雖然官網文檔鼓勵使用 Executors 提供的競態方法創建線程池,但是面對具體的應用場景,可能不是最優的選擇。儘量還是需要了解 ThreadPoolExecutor 各個參數做用,以便定製化配置線程池。
ThreadPoolExecutor 的線程數會在和核心線程數和最大線程數之間自動調節。如果線程數在少於核心線程數的情況下提交任務,那麼不管是否存在其他空閒線程,線程池會創建一個線程來執行該任務;如果在線程數位於核心線程數和最大線程數之間的情況下提交任務,那麼任務會先嚐試進入阻塞隊列,等待線程空閒出來按照順序執行阻塞隊列中的任務。如果阻塞隊列已經達到容量上限,那麼線程池會創建新的線程來執行該任務;如果在線程池的線程數已經達到最大線程數時,那麼嘗試進入阻塞隊列等待執行。如果阻塞隊列也達到上限,那麼執行線程池初始化時選擇的拒絕策略。如果設置的核心線程數和最大線程數是一樣的值,那麼相當於設置了一個固定線程數的線程池。核心線程數和最大線程數通常在初始化時就確定,但是可以通過提供的改變數值的方法(setCorePoolSize,setMaximumPoolSize)來修改。
ThreadPoolExecutor 默認是基於請求來創建線程的,即只有當請求來時纔會創建線程。ThreadPoolExecutor 允許通過重寫 prestartCoreThread 和 prestartAllCoreThreads 來實現初始化即創建線程。這個功能對於那些配置了沒有容量的阻塞隊列會比較有用。
ThreadPoolExecutor 利用 ThreadFactory 來創建線程。如果沒有做特殊配置,那麼利用的是 Executors.defaultThreadFactory(ThreadFactory 是接口)來具體創建線程。Executors.defaultThreadFactory 創建的線程都在一個線程組(ThreadGroup)中,並且擁有同樣的優先級別,並且都不是守護線程。可以自定義 ThreadFactory 來改變線程的名稱,優先級別,守護線程狀態等。如果 ThreadFactory 創建線程失敗雖然不會影響任務的提交,但是會出現沒有線程執行任務的情況。線程應當要持有 modifyThread 的權限。如果線程沒有該權限,服務會被降級,將會導致:配置改變後不會立即產生影響;關閉線程池時,線程池允許關閉但是不會立刻結束。
如果當前的線程數超過核心線程數並且存在線程處於空閒狀態(沒有執行任務),當空閒時間超過了 keepAliveTime,此類線程會被線程池回收。如果未來有同時超過核心線程數的任務,新的線程會被創建來執行這些任務。keepAliveTime(空閒時間)同樣也允許在初始化之後被修改並生效。keepAliveTime 如果設置成 Long.MAXVALUE 會當成沒有過期時間,空閒線程將不會被自動關閉。keepAliveTime 的策略默認只對超過核心線程數的空閒線程起作用,但是可以設置 allowCoreThreadTimeOut 來對空閒的核心線程起作用。
ThreadPoolExecutor 內部存在一個阻塞隊列用於存放沒有足夠線程執行的任務。如果當前的線程數少於核心線程數的情況下提交任務時,ThreadPoolExecutor 會創建新線程執行該任務;如果執行任務的線程數已經大於核心線程數,那麼任務會存到阻塞隊列中。阻塞隊列能夠存儲的任務數量取決於阻塞隊列的容量大小。
ThreadPoolExecutor 使用的是 BlockingQueue 作爲阻塞隊列,BlockingQueue 有三個重要實現:
- SynchronousQueue: 直接傳遞。這是一個由提交任務的線程直接傳遞給消費任務的線程的傳遞方式。如果沒有任何消費線程,那麼向 SynchronousQueue 提交任務會失敗。所以選擇 SynchronousQueue 作爲阻塞隊列時,每次提交任務如果沒有當前沒有線程能夠處理任務需要創建新線程來及時處理該任務。該策略可以避免如果有大量任務,並且任務內部有依賴關係而導致的鎖問題(正在活躍線程中執行的任務依賴由於超過了核心線程數導致在阻塞隊列中排隊的任務,出現被依賴優先執行的任務沒有線程執行,而執行中的任務由於被依賴任務沒有執行完畢導致線程不釋放)。SynchronousQueue 一般要求提供不設限制的容量,避免出現達到容量上限導致無法創建線程來處理任務而導致的任務提交失敗。但是如果消費任務的速度少於任務提交的速度,SynchronousQueue 會出現由於創建的線程過多而導致的內存溢出問題。
- LinkedBlockingQueue:無界隊列。隊列不會在初始化時定義隊列的容量限制,最大上限時 Integer.MAXVALUE。只要少於這個值,隊列將不會拒絕將任務添加到隊列中。如果使用 LinkedBlockingQueue,由於一般很能將任務加到 Integer.MAXVALUE,所以會出現只要線程數達到核心線程數,線程將不會被創建(任務都被存到 LinkedBlockingQueue 中)。LinkedBlockingQueue 適合任務處理非常快的場景,但是如果任務處理不及時,和可能會導致任務大量在 LinkedBlockingQueue 中堆積,導致大量超時。由於 LinkedBlockingQueue 很難達到隊列上限,所以也會導致線程池的最大線程數的配置不生效。
- ArrayBlockingQueue:有界隊列。初始化時需要指定隊列的容量限制。有界隊列可以幫助避免資源耗盡,但是也更加難以控制。有界隊列的容量限制和線程池線程數的配置會相互影響。如果配置了一個大容量的阻塞隊列和比較小的線程數上限,可以減少線程上下文切換的開銷提高 CPU 的使用效率,但是可能會降低請求的吞吐量。而且如果線程數過少並且過多的阻塞在 IO 操作等等待或者耗時比較長的任務上,那麼會導致大量任務沒有線程執行堆積在阻塞隊列上,長時間得不到執行甚至會導致請求方等待超時。如果使用一個比較小的隊列配置一個較大的線程數上限,可以提高 CPU 的效率。但是如果頻繁的在多個線程之間來回切換也可能會降低吞吐量。
ThreadPoolExecutor 在隊列已經滿了並且線程已經全部執行並且線程數已經到達限制的上限或者已經關閉 ThreadPoolExecutor 對任何提交的任務都會執行拒絕策略。默認使用 AbortPolicy 拒絕策略。
- AbortPolicy:該策略拋出 RejectedExecutionException 異常
- CallerRunsPolicy:該策略會讓提交任務的線程計算提交的任務。
- DiscardPolicy:直接丟棄任務。
- DiscardOldestPolicy:丟棄隊列第一個任務,重新執行提交邏輯。不斷重複以上步驟知道提交成功。
如果以上的拒絕策略不滿足業務需求,可以自定義一個拒絕策略。但是在設計拒絕策略是在 ThreadPoolExecutor 達到拒絕條件下才會被觸發。
ThreadPoolExecutor 提供了一些鉤子方法(某種場景下觸發,如果有特殊需求可以重寫這些鉤子方法達到業務目的),比如 beforeExecute 和 afterExecute。他們分別在任務執行前和任務執行結束後執行。如果需要統計、日誌等可以重寫這倆方法實現。如果 ThreadPoolExecutor 關閉需要做二外的事情可以重寫 terminated 方法來實現。
如果鉤子方法,比如 afterExecute,執行失敗,雖然具體的任務內容已經執行,但是總體上該任務執行失敗。
ThreadPoolExecutor 如果不被手動關閉,那麼默認情況下 ThreadPoolExecutor 之前創建過的不多於核心線程數的線程。如果擔心忘記手動關閉而導致此類資源無法回收,可以設置 allowCoreThreadTimeOut,那麼核心線程數如果空閒時間超過 getKeepAliveTime,線程池也會將此類線程資源回收。
重要組件介紹
ReentrantLock mainLock
ThreadPoolExecutor 內部持有一個 ReentrantLock,爲關閉 ThreadPoolExecutor,等待關閉 ThreadPoolExecutor,中斷空閒線程,添加工作線程等需要保證線程安全的操作提供同步執行的保證。
Worker
Worker 對象表示一個工作線程。Worker 本身繼承自 AbstractQueuedSynchronizer(同步器的實現以及大部分 JUC 鎖的父類),這表示 Worker 本身可以綁定一個線程(exclusiveOwnerThread 屬性);內部維護了一個單向鏈表可以按照某個策略存儲一系列任務;天然支持鎖的機制。
Worker 在執行任務前後會上鎖和解鎖的操作。利用 AbstractQueuedSynchronizer 的上鎖和釋放鎖而不是使用 ReentrantLock 做上鎖和釋放鎖的原因是:Worker 在等待任務執行結束時被終止。如果其他線程調用 setCorePoolSize,那麼會執行一些列操作 setCorePoolSize->interruptIdleWorkers->tryLock() (here success!) -> Thread.interrupt (thread of this worker)。所以 Worker 不能和 ReentrantLock 共用一把鎖(Why Worker extends AbstractQueuedSynchronizer)。也因此 Worker 被設計爲是獨佔模式(一次只能一個線程持有)非重入鎖。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
// 具體的工作線程。如果線程工廠創建線程失敗則指向 null
final Thread thread;
// 初始化時指定的任務。一般情況下初始化時指定的是 null
Runnable firstTask;
// 執行過的任務數量
volatile long completedTasks;
/**
* 構造方法。根據指定的任務初始化 Worker。
* @param firstTask 第一個任務。如果沒有則 null
*/
Worker(Runnable firstTask) {
// 初始化時將 state 的值設置爲-1,如此除非調用釋放鎖,否則無法獲取鎖。如此該 Worker 無法被直接中斷。
setState(-1);
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 執行任務。調用 ThreadPoolExecutor.runWorker
public void run() {
runWorker(this);
}
// 配置成爲排它鎖
protected boolean isHeldExclusively() {
return getState() != 0;
}
// 獲取鎖
protected boolean tryAcquire(int unused) {
// CAS 比較 state 的值,如果能夠將其從 0 修改成 1 則表示能夠獲得鎖。
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 釋放鎖
protected boolean tryRelease(int unused) {
// 持有同步器的線程置空
setExclusiveOwnerThread(null);
// 同步器狀態置爲 0
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
// 關閉當前任務
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
BlockingQueue workQueue
workQueue 阻塞隊列,是爲來不及及時消費的任務提供臨時存儲的緩衝池。在提交任務時(execute(Runnable command)),如果當前執行任務的線程數已經大於或者等於核心線程數,那麼會將任務存儲到阻塞隊列中,等待線程空閒出來執行隊列中的任務。如果此時隊列已經滿了,那麼將會繼續添加工作線程(addWorker)。但是如果執行任務的線程數已經到達線程池線程數上限,那麼將會拒絕任務(reject(command))。
workQueue 還在Wroker 執行任務的過程中(runWorker)不斷提供任務(while (task != null || (task = getTask()) != null))。getTask即從阻塞隊列中不斷獲取任務,如果隊列中沒有任務,那麼阻塞在等待任務中。此時Worker變成空閒工作線程,超過keepAliveTime時間後可能會被回收。
Condition termination
termination 是 mainLock(ReentrentLock)提供的競態條件,用於提供對 awaitTermination 的支持。如果 ThreadPoolExecutor 被關閉(調用 shutDown 或者 shutDownNow)或者需要被關閉時,都會調用 tryTerminate()觸發 termination.signalAll()。而 awaitTermination 方法會調用 termination.awaitNanos(nanos),阻塞等待指定的時間。如果在指定時間內收到關閉完成的信號(termination.signalAll())或者等待超時則返回 true 或者 false。
awaitTermination 的使用場景如下:
// 關閉線程池
pool.shutdown();
try {
// 等待線程池關閉完成。如果線程池沒有在指定時間內完成關閉(還有任務在執行),那麼使用 shutdownNow(正在執行的任務也會被關閉)關閉線程池。
if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
pool.shutdownNow(); // Cancel currently executing tasks
// 繼續等待線程池被關閉,如果在等待時間內還未被關閉,那麼記錄異常
if (!pool.awaitTermination(60, TimeUnit.SECONDS))
System.err.println("Pool did not terminate");
}
} catch (InterruptedException ie) {
// 如果當前調用關閉線程池的線程被中斷,那麼繼續關閉線程池,並將線程中斷標識置爲中斷狀態(捕獲到 InterruptedException 後線程的中斷標誌會被清空)。
pool.shutdownNow();
Thread.currentThread().interrupt();
}
ThreadFactory threadFactory
ThreadPoolExecutor 中使用的線程都是 ThreadFactory 創建的。Executors 提供了兩種實現:
- DefaultThreadFactory:能夠創建在用一個線程組和擁有同樣命名規則的線程。
- PrivilegedThreadFactory:繼承 DefaultThreadFactory,額外增加了擁有可以訪問調用線程的上下文權限(擁有相同的 AccessControlContext 和 ClassLoader)。
RejectedExecutionHandler handler
handler 是阻塞滿了,執行任務的線程數也已經達到配置的上限,那麼對新接入的任務應當如何執行的策略。
public interface RejectedExecutionHandler {
/**
* 當線程池 ThreadPoolExecutor 的隊列無法存儲更多任務,並且線程已經達到上限時會調用該方法會調用該方法。
* 如果隊列已經關閉也可能會調用該方法
*
* @param r 任務
* @param executor 線程池
* @throws RejectedExecutionException 如果沒有任何補救措施那麼拋出該異常
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
ThreadPoolExecutor 提供了四種實現:
- AbortPolicy:沒有不就措施,拋出 RejectedExecutionException 異常
- DiscardPolicy:直接丟棄任務,不做任何處理
- DiscardOldestPolicy:如果線程池沒有關閉,那麼丟棄阻塞隊列中的第一個元素(最早進入隊列中的任務)並提交任務(execute)。如果依然無法添加,繼續執行 DiscardOldestPolicy 策略(不斷循環直到添加成功)。
- CallerRunsPolicy:讓提交任務的線程執行任務。
execute 方法原理
ThreadPoolExecutor 利用 AtomicInteger ctl 來記錄工作線程數和線程池狀態。ThreadPoolExecutor 基於 ctl 維護了兩個屬性:
- workerCount:工作線程數
- runState:ThreadPoolExecutor 的狀態,包括運行中,關閉等。
爲了讓 workerCount 和 runState 都維護在一個值上(ctl),限制了 workerCount 上限只有 229-1,而不是 int 的最大範圍。如果這個限制太小不滿足需求,那麼將來可以使用 AtomicLong 代替。使用 int 會使代碼簡單一些,速度也快一些。
workerCount 表示那些
ThreadPoolExecutor 的執行任務的流程如下:
源碼介紹:
/**
* 執行提交的任務。通常情況,線程池會使用維護的線程異步執行提交的任務,並確在滿足拒絕執行的情況下執行拒絕線程池初始化時選擇的拒絕策略。
* 如果線程池被關閉,同樣也無法執行任務,同樣也會執行拒絕策略。
* @param command 任務
* @throws RejectedExecutionException 如果滿足拒絕執行的條件,並且沒有任何補救措施則會拋出該異常
* @throws NullPointerException 如果提交的任務是 Null,則拋出該異常
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. 如果當前執行任務的線程數少於核心線程數,那麼線程池會創建一個新的工作線程來執行當前提交的任務,即創建一個 Worker 的操作(addWorker)。創建 worker 的操作會再次確認線程池的狀態,如果線程已經關閉或者已經達到上限,那麼創建失敗並返回 false。
*
* 2. 如果工作線程數大於或者等於核心線程數或者 addWorker 操作失敗(返回 false),那麼校驗線程池未被關閉並且將任務成功提交給阻塞隊列。如果校驗通過,double-check 線程池狀態。如果線程池關閉,則刪除任務並執行拒絕策略;否則如果工作線程數是 0 則添加工作線程(addWorker)。
*
* 3. 如果將任務添加到阻塞隊列中失敗,那麼執行 addWorker 操作添加工作線程。如果也同樣失敗則執行拒絕策略(說明已經是阻塞隊列滿並且工作線程數達到最大線程數的狀態)。
*/
// 當前工作線程數
int c = ctl.get();
// 如果 worker 的數量小於核心線程數,那麼添加 worker
if (workerCountOf(c) < corePoolSize) {
// 添加 worker 操作還會做 double-check,如果添加成功則退出,否則後續做入隊操作
if (addWorker(command, true))
return;
// 如果添加失敗,那麼獲取最新的 worker 數量
c = ctl.get();
}
// 如果線程未關閉,那麼執行入隊操作
if (isRunning(c) && workQueue.offer(command)) {
// 如果正確入隊,再次確認線程池屬於未關閉狀態。
// 如果線程池關閉,那麼刪除剛入隊的任務,並執行拒絕策略
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果線程池未關閉或者刪除任務失敗,校驗線程池中的工作線程,如果不存在則添加工作線程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 入隊失敗(隊列已經滿了),再次執行添加 worker 的操作(如果超過核心線程數並且隊列已經達到容量上限,在未超過最大線程數的配置下,會創建新線程處理)。
else if (!addWorker(command, false))
// 添加 worker 失敗,可能是已經達到最大線程數,那麼執行拒絕策略。
reject(command);
}
改變線程池狀態的方法
ThreadPoolExecutor的狀態信息存儲在屬性AtomicInteger ctl中,ctl值的範圍是在-536870912到1610612736之間。爲了保證線程池的狀態變更是線程安全的,所以使用AtomicInteger存儲狀態。其中0表示SHUTDOWN狀態。-536870912~0之間表示RUNNING。-536870912表示沒有任何工作線程在運行,以後沒添加一個工作線程,ctl+1。所以ThreadPoolExecutor最多允許的線程數是536870911個線程。大於0的值只能是536870912,表示停止(STOP);1073741824表示關閉中(TIDYING);1610612736表示終止(TERMINATED)。
之所以採用這種方式是方便採用位運算快速轉換各個狀態的值,比如:
// 當前線程池的狀態 RUNNING(-536870912~0),SHUTDOWN(0~536870912),STOP(536870912~1073741824),TIDYING(1073741824),TERMINATED(1610612736)
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 當前工作線程的數量。只有ctl的值在-536870912~0(不包括0)之間纔有工作線程,並且數量在0~536870911(包括0)之間
private static int workerCountOf(int c) { return c & CAPACITY; }
// 將對應的狀態轉成
private static int ctlOf(int rs, int wc) { return rs | wc; }
ThreadPoolExecutor大致有以下的幾種轉變:
- RUNNING -> SHUTDOWN:運行的線程池調用shutdown方法
- (RUNNING or SHUTDOWN) -> STOP:運行的狀態或者關閉狀態調用shutdownNow()方法
- SHUTDOWN -> TIDYING:關閉狀態下阻塞隊列是空隊列並且沒有任何工作線程會轉變成TIDYING狀態
- STOP -> TIDYING:線程池沒有工作線程停轉狀態會轉變成TIDYING
- TIDYING -> TERMINATED:terminated()的鉤子方法執行結束後狀態變成TERMINATED
以下是轉變狀態的方法:
/**
* 將狀態變更成指定的狀態
*
* @param targetState 指定狀態。ThreadPoolExecutor裏是SHUTDOWN或者STOP。
*/
private void advanceRunState(int targetState) {
//無限循環直到更新成功
for (;;) {
// 當前狀態碼
int c = ctl.get();
// 如果當前狀態碼已經大於給的狀態碼,那麼無需修改跳出循環。
// CAS修改,將狀態碼改成狀態碼和工作線程數或運算後的結果。這是STOP的狀態位於536870912~1073741824(不包括1073741824)的原因。即如果當前工作線程數是2,那麼變更後的結果是536870914。
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
/**
* 終止線程池
*/
final void tryTerminate() {
for (;;) {
// 當前狀態碼
int c = ctl.get();
// 如果當前狀態還是運行中的狀態,那麼直接跳出結束。狀態變化不允許RUNNING -> TIDYING和RUNNING -> TERMINATED
// 如果狀態已經是TIDYING或者TERMINATED,那麼不需要做任何處理,直接退出方法
// 如果狀態是SHUTDOWN,並且阻塞隊列還有任務未執行,那麼直接退出等待阻塞隊列中的任務執行結束。
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 說明已經處於關閉線程池的過程中,但是此時還有工作線程。那麼關閉一個空閒工作線程(也可能不存在),目的在於通知其他所有阻塞等待獲取任務的空閒工作線程退出(發送關閉信號),所以不需要關閉所有的空閒線程。
// 發送信號的邏輯是:當關閉一個工作線程(interrupt()),會使得Worker執行跳出循環等待獲取任務並進入processWorkerExit()方法,該方法會繼續調用tryTerminate通知其他空閒的工作線程關閉(見runWorker)
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
// 上
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 將線程池CAS變更成TIDYING狀態
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 變更成功調用terminated()鉤子方法
terminated();
} finally {
// 鉤子方法執行結束(或者異常中斷)後將線程池變更成TERMINATED狀態。
ctl.set(ctlOf(TERMINATED, 0));
// 發送關閉線程池結束信號(awaitTermination方法會阻塞直到該通知或者超時)
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
工作線程控制任務中斷的方法
ThreadPoolExecutor會回收那些超長空閒的工作線程或者強制關閉時回收正在進行中的工作線程,所以定義了一些私有方法用於終止這些工作線程
/**
* 校驗關閉工作線程的權限
* 如果有安全管理器,校驗調用線程是否有中斷工作線程(Worker)綁定的線程(Thread)的權限。
*/
private void checkShutdownAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
/**
* 中斷所有的工作線程
*/
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
/**
* 中斷空閒的工作線程。
* 空閒的工作線程是指當前阻塞等待獲取任務(w.tryLock()拿不到鎖)的工作線程。
* 如果onlyOne爲true,那麼便利到一個工作線程則退出。
* onlyOne爲true的場景是tryTerminate通過關閉一個空閒的工作線程來通知關閉信號。
*/
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
/**
* 關閉所有的空閒工作線程
*/
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
工作線程添加、運行和退出等方法
/**
* 添加一個工作線程
* 如果線程池的狀態是RUNNING,並且未超過線程數量限制(如果core是true則不應該超過核心線程數限制,否則是最大線程數限制)並且線程池成功創建線程,那麼會添加一個工作線程。工作線程會立即開始工作,並首先執行指定的第一個任務。後續會通過getTask從阻塞隊列中獲取任務並執行,否則阻塞在getTask中。
*
* @param firstTask 工作線程第一個任務。如果不存在則給null。這個設定是爲了解決在線程數量少於核心線程數時,任務可以不用進入等待隊列執行讓工作線程執行;或者隊列已經滿了,此時無法進入隊列。
*
* @param core 是否創建核心線程數內的線程
* @return 如果工作線程創建成功則返回true,否則返回false
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 狀態嗎
int c = ctl.get();
// 狀態 RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED中的一種。採用和CAPACITY補碼做與運算,0~536870912之間的數值都會得到0(SHUTDOWN)的結果。
int rs = runStateOf(c);
// 如果狀態不是RUNNING,並且不屬於是SHUTDOWN並且隊列還有任務的情況,那麼返回false。無需添加工作線程。如果是SHUTDOWN但是隊列還有任務未執行,那麼還需要工作線程繼續執行隊列中的任務,此種情況下允許繼續添加工作線程,並且工作線程只用於執行隊列中的任務。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 如果是RUNNING狀態,那麼狀態碼加一
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
// 獲取最新的狀態,再次校驗線程池狀態是否發生變化
c = ctl.get();
if (runStateOf(c) != rs)
continue retry;
// compareAndIncrementWorkerCount 更新失敗,重新獲取最新的重新走循環更新工作線程數
}
}
// 工作線程是否開始工作
boolean workerStarted = false;
// 工作線程是否添加成功
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 線程池最新的狀態,用於再次確認
int rs = runStateOf(ctl.get());
// 再次校驗工作線程是否滿足啓動的條件
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 如果線程是已經啓動的狀態那麼拋出異常。剛創建的線程應該是未啓動的狀態
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 如果工作線程添加成功,那麼讓工作線程開始工作。
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 如果工作線程未開始工作,那麼執行回退操作
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
/**
* 添加工作線程失敗後的回退操作
* - 從隊列中刪除之前添加的工作線程
* - 工作線程數減一
* - 執行tryTerminate方法,校驗是否需要終止線程池。如果線程池在調用addWorkerFailed前一刻被終止,由於workers中還有當前的工作線程,所以無法變成TIDYING和TERMINATED狀態。
*/
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
/**
* 工作線程退出方法
* 如果工作線程在執行過程中遇到異常或者線程被中斷或者超長空閒(getTask() == null),那麼執行銷燬工作線程任務。
* 如果是由於異常或者線程中斷導致的退出,並且線程池狀態是RUNNING或者SHUTDOWN(SHUTDOWN狀態依然需要工作線程執行隊列中的任務),那麼需要補充一個工作線程。
*
* @param w 工作線程
* @param completedAbruptly 是否由於是異常(包括線程中斷)導致的退出
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果是異常退出導致需要關閉工作線程,那麼之前創建工作線程時已經添加過工作線程數量(狀態碼加一),那麼需要現在需要減少工作線程數量
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
// 校驗是否需要關閉線程池
tryTerminate();
int c = ctl.get();
// 如果是RUNNING或者SHUTDOWN狀態並且當前需要工作線程那麼創建一個工作線程
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);
}
}
/**
* 獲取任務
* 工作線程在執行完第一個任務(初始化給定的)後,會阻塞等待阻塞隊列給出一個新的任務。
*
* @return 阻塞隊列中的任務。如果線程池已經被關閉或者超時,那麼返回null
*/
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
// 狀態碼
int c = ctl.get();
// 狀態
int rs = runStateOf(c);
// 如果線程池是SHUTDOWN狀態並且阻塞隊列是空隊列(SHUTDOWN狀態依然需要處理阻塞隊列中的任務)或者線程池是STOP及後續的狀態那麼表示當前工作線程需要關閉,減少工作線程數量並返回null。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 工作線程數量
int wc = workerCountOf(c);
// 是否允許工作線程超長空閒關閉並且線程數量大於核心線程數
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 當前工作線程滿足超長空閒關閉的條件,那麼工作線程數量減一併返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
// 從阻塞隊列獲取任務
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
// 如果工作線程的線程被中斷碼,那麼重新獲取任務
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
/**
* 工作線程開始執行任務
* 不斷從阻塞隊列中獲取任務並執行。
* 如果線程池關閉,那麼回收工作線程。
* 如果是執行任務發送異常或者線程被中斷導致工作線程綁定的線程被終止並回收,那麼根據情況(當前線程池是否需要一個新的工作線程)創建一個新的工作線程代替原來的工作線程。
*
* @param w the worker
*/
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 釋放鎖。工作線程初始化時會將state設置爲-1,如此終止空閒工作線程的方法不會關閉這些剛初始化的工作線程。
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 不斷從阻塞隊列中獲取任務並執行。
// 如果線程池的狀態是SHUTDOWN並且阻塞隊列是空隊列或者狀態是SHUTDOWN以後的狀態,那麼getTask返回null,表示需要關閉當前工作線程。
// 如果工作線程長時間未能從阻塞隊列中獲取任務,getTask也可能返回null,也將會關閉工作線程。
while (task != null || (task = getTask()) != null) {
// 獲取到任務,工作線程上鎖,表示並非是空閒的工作線程。
w.lock();
// 如果線程池已經是SHUTDOWN後續狀態(所有的工作線程都需要被關閉)並且當前工作線程沒有被中斷,那麼中斷工作線程綁定的線程。
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
// 滿足條件中斷工作線程綁定(Worker)的線程(Thread),執行finally中的processWorkerExit,關閉工作線程
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();
}
}
// 執行任務時未發生異常。這裏是getTask返回null,工作線程需要被關閉。
completedAbruptly = false;
} finally {
// 回收工作線程
processWorkerExit(w, completedAbruptly);
}
}
其他方法的源碼
/**
* 關閉線程池。該方法不會立即停止已經提交的任務,但是會拒絕接受新的任務。如果對已經關閉的線程池調用該方法不會產生任何效果。
* 該方法不會阻塞,即該方法執行結束後不代表線程池關閉完成。如果希望獲得關閉完成的通知,可以使用 awaitTermination。
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 如果存在 SecurityManager,那麼校驗調用線程是否有“modifyThread”的 RuntimePermission
checkShutdownAccess();
// while true + cas更新,將線程池狀態更新成關閉(shutdown)狀態
advanceRunState(SHUTDOWN);
// 關閉沒有執行任務的工作線程(worker)
interruptIdleWorkers();
// 調用關閉線程的鉤子方法。一般用於統計類場景。
onShutdown();
} finally {
mainLock.unlock();
}
// 如果當前狀態不是(shutdown並且worker隊列是空隊列)並且不是(stop並且worker隊列是空隊列),嘗試更新成終止狀態
tryTerminate();
}
/**
* 關閉所有的工作線程,並終止等待任務,並返回等待執行的任務列表。
* 終止任務是通過線程的interrupt方法實現的。如果線程不響應中斷信號,那麼任務可能無法被中斷。
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 校驗線程的訪問權限
checkShutdownAccess();
// 將線程池狀態改成stop狀態
advanceRunState(STOP);
// 終止所有工作線程
interruptWorkers();
// 將阻塞隊列中的任務取出並清空阻塞隊列
tasks = drainQueue();
} finally {
mainLock.unlock();
}
// 如果當前狀態不是(shutdown並且worker隊列是空隊列)並且不是(stop並且worker隊列是空隊列),嘗試更新成終止狀態
tryTerminate();
return tasks;
}
// 一切不屬於運行中的狀態都是關閉狀態。
public boolean isShutdown() {
return ! isRunning(ctl.get());
}
/**
* 如果線程池已經開始關閉,但是還未完成,那麼返回true。即狀態位於RUNNING和TERMINATED之間
*
* @return {@code true} if terminating but not yet terminated
*/
public boolean isTerminating() {
int c = ctl.get();
return ! isRunning(c) && runStateLessThan(c, TERMINATED);
}
// 如果當前是TERMINATED狀態則返回true,否則返回false
public boolean isTerminated() {
return runStateAtLeast(ctl.get(), TERMINATED);
}
/**
* 等待線程池關閉結束。該方法回阻塞線程知道線程池關閉結束或者超時。
* 一般用於關閉線程池。線程池shutdown後等待一段時間,如果線程池還未關閉,那麼使用shutdownNow關閉線程池。
* @param timeout 超時時間
* @param unit 時間單位
*/
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (;;) {
// 比較當前狀態是否>=TERMINATED,如果是則返回true
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
// 說名當前還未處於TERMINATED狀態,那麼查看是否超時。如果超時則返回false。
if (nanos <= 0)
return false;
// 未超時並且不處於TERMINATED狀態,那麼阻塞等待關閉結束通知。
nanos = termination.awaitNanos(nanos);
}
} finally {
mainLock.unlock();
}
}
/**
* 重寫回收線程池對象的方法,回收時關閉線程池。
*/
protected void finalize() {
SecurityManager sm = System.getSecurityManager();
if (sm == null || acc == null) {
shutdown();
} else {
PrivilegedAction<Void> pa = () -> { shutdown(); return null; };
AccessController.doPrivileged(pa, acc);
}
}
/**
* Sets the thread factory used to create new threads.
*
* @param threadFactory the new thread factory
* @throws NullPointerException if threadFactory is null
* @see #getThreadFactory
*/
public void setThreadFactory(ThreadFactory threadFactory) {
if (threadFactory == null)
throw new NullPointerException();
this.threadFactory = threadFactory;
}
/**
* Returns the thread factory used to create new threads.
*
* @return the current thread factory
* @see #setThreadFactory(ThreadFactory)
*/
public ThreadFactory getThreadFactory() {
return threadFactory;
}
/**
* Sets a new handler for unexecutable tasks.
*
* @param handler the new handler
* @throws NullPointerException if handler is null
* @see #getRejectedExecutionHandler
*/
public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
if (handler == null)
throw new NullPointerException();
this.handler = handler;
}
/**
* Returns the current handler for unexecutable tasks.
*
* @return the current handler
* @see #setRejectedExecutionHandler(RejectedExecutionHandler)
*/
public RejectedExecutionHandler getRejectedExecutionHandler() {
return handler;
}
/**
* 調整核心線程數
*
* @param corePoolSize 核心線程數
* @throws IllegalArgumentException 如果數值小於0,那麼拋出該異常
* @see #getCorePoolSize
*/
public void setCorePoolSize(int corePoolSize) {
if (corePoolSize < 0)
throw new IllegalArgumentException();
int delta = corePoolSize - this.corePoolSize;
this.corePoolSize = corePoolSize;
// 如果工作線程數超過了新設置的核心線程數,那麼關閉空閒工作線程。
if (workerCountOf(ctl.get()) > corePoolSize)
interruptIdleWorkers();
// 如果工作線程數小於或者等於核心線程數,那麼嘗試添加新的工作線程
else if (delta > 0) {
// 需要新增的工作線程數量。取差值和阻塞隊列的長度的最小值
int k = Math.min(delta, workQueue.size());
// 添加工作線程
while (k-- > 0 && addWorker(null, true)) {
if (workQueue.isEmpty())
break;
}
}
}
/**
* 返回核心線程數
*
* @return the core number of threads
* @see #setCorePoolSize
*/
public int getCorePoolSize() {
return corePoolSize;
}
/**
* 啓動一個核心線程。只有當工作線程數小於核心線程數時才能夠啓動成功
*
* @return {@code true} if a thread was started
*/
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}
/**
* 啓動一個工作線程。只有當工作線程數小於最大線程數時纔會啓動成功
*/
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
/**
* 將線程啓動到核心線程數的個數
*
* @return 返回本次操作啓動工作線程的個數
*/
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
/**
* 如果允許核心線程數內的工作線程由於長時間空閒而關閉,那麼返回true,否則返回false
*
* @return 如果允許長時間空閒而關閉則返回true,否則返回false
*
* @since 1.6
*/
public boolean allowsCoreThreadTimeOut() {
return allowCoreThreadTimeOut;
}
/**
* 設置是否允許核心線程數內的工作線程空閒時間超過指定的最大空閒時間後關閉該工作線程。
*
* @param value true表示允許核心線程數內的工作線程超時空閒關閉
* @throws IllegalArgumentException 如果允許關閉但是未設置空閒時間,那麼拋出該異常
*
* @since 1.6
*/
public void allowCoreThreadTimeOut(boolean value) {
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
if (value != allowCoreThreadTimeOut) {
allowCoreThreadTimeOut = value;
// 如果允許超長空閒關閉,那麼立即清理空閒的工作線程
if (value)
interruptIdleWorkers();
}
}
/**
* 設置最大線程數
*
* @param maximumPoolSize 最大線程數
* @throws IllegalArgumentException 如果最大線程數小於等於0或者小於核心線程數,那麼拋出該異常
* @see #getMaximumPoolSize
*/
public void setMaximumPoolSize(int maximumPoolSize) {
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize)
throw new IllegalArgumentException();
this.maximumPoolSize = maximumPoolSize;
// 如果當前的工作線程數已經超過最大線程數,那麼關閉空閒的工作線程
if (workerCountOf(ctl.get()) > maximumPoolSize)
interruptIdleWorkers();
}
/**
* 返回最大線程數
*
* @return 最大線程數
* @see #setMaximumPoolSize
*/
public int getMaximumPoolSize() {
return maximumPoolSize;
}
/**
* 設置空閒時間。
* 超過核心線程數的工作線程如果空閒時間超過設置的空閒時間,那麼此類工作線程將會被關閉。
* 如果設置了允許核心線程數內的工作線程超長空閒時間關閉(allowCoreThreadTimeOut == true),
* 那麼所有工作線程空閒時間超過設置的空閒時間(keepAliveTime)都將會被關閉。
*
* @param time 空閒時間
* @param unit 時間單位
* @throws IllegalArgumentException 如果小於等於0則拋出該異常
* @see #getKeepAliveTime(TimeUnit)
*/
public void setKeepAliveTime(long time, TimeUnit unit) {
if (time < 0)
throw new IllegalArgumentException();
if (time == 0 && allowsCoreThreadTimeOut())
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
long keepAliveTime = unit.toNanos(time);
long delta = keepAliveTime - this.keepAliveTime;
this.keepAliveTime = keepAliveTime;
if (delta < 0)
interruptIdleWorkers();
}
/**
* 返回設置的空閒時間
*
* @param unit 返回時間的時間單位
* @return 空閒時間
* @see #setKeepAliveTime(long, TimeUnit)
*/
public long getKeepAliveTime(TimeUnit unit) {
return unit.convert(keepAliveTime, TimeUnit.NANOSECONDS);
}