目錄
一、任務的提交過程
1.1 submit方法源碼
public Future<?> submit(Runnable task) {
if (task == null)
throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null)
throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
submit
的實現方法位於抽象類 AbstractExecutorService
中,而此時 execute
方法還未實現(而是在 AbstractExecutorService
的繼承類 ThreadPoolExecutor
中實現)。submit
有三種重載方法,這裏我選取了兩個常用的進行分析,可以看出無論哪個submit
方法都最終調用了 execute
方法。
1.2 execute方法源碼
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* clt記錄着runState和workerCount
*/
int c = ctl.get();
/*
* workerCountOf方法取出低29位的值,表示當前活動的線程數;
* 如果當前活動線程數小於corePoolSize,則新建一個線程放入線程池中;
* 並把任務添加到該線程中。
*/
if (workerCountOf(c) < corePoolSize) {
/*
* addWorker中的第二個參數表示限制添加線程的數量是根據corePoolSize來判斷還是maximumPoolSize來判斷;
* 如果爲true,根據corePoolSize來判斷;
* 如果爲false,則根據maximumPoolSize來判斷
*/
if (addWorker(command, true))
return;
/*
* 如果添加失敗,則重新獲取ctl值
*/
c = ctl.get();
}
/*
* 如果當前線程池是運行狀態並且任務添加到隊列成功
*/
if (isRunning(c) && workQueue.offer(command)) {
// 重新獲取ctl值
int recheck = ctl.get();
// 再次判斷線程池的運行狀態,如果不是運行狀態,由於之前已經把command添加到workQueue中了,
// 這時需要移除該command
// 執行過後通過handler使用拒絕策略對該任務進行處理,整個方法返回
if (! isRunning(recheck) && remove(command))
reject(command);
/*
* 獲取線程池中的有效線程數,如果數量是0,則執行addWorker方法
* 這裏傳入的參數表示:
* 1. 第一個參數爲null,表示在線程池中創建一個線程,但不去啓動;
* 2. 第二個參數爲false,將線程池的有限線程數量的上限設置爲maximumPoolSize,添加線程時根據maximumPoolSize來判斷;
* 如果判斷workerCount大於0,則直接返回,在workQueue中新增的command會在將來的某個時刻被執行。
*/
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/*
* 如果執行到這裏,有兩種情況:
* 1. 線程池已經不是RUNNING狀態;
* 2. 線程池是RUNNING狀態,但workerCount >= corePoolSize並且workQueue已滿。
* 這時,再次調用addWorker方法,但第二個參數傳入爲false,將線程池的有限線程數量的上限設置爲maximumPoolSize;
* 如果失敗則拒絕該任務
*/
else if (!addWorker(command, false))
reject(command);
}
由於execute方法中多次調用 addWorker
,我們這裏就簡要介紹一下它,這個方法的主要作用就是創建一個線程來執行Runnnable對象。
addWorker(Runnable firstTask, boolean core)
第一個參數 firstTask
不爲null,則創建的線程就會先執行 firstTask對象,然後去阻塞隊列中取任務,否直接到阻塞隊列中獲取任務來執行。
第二個參數,core
參數爲真,則用 corePoolSize 作爲池中線程數量的最大值;
爲假,則以maximumPoolSize
作爲池中線程數量的最大值。
簡要分析一下execute源碼,執行一個Runnable對象時,首先通過workerCountOf(c)
獲取線程池中線程的數量,如果池中的數量小於 corePoolSize
就調用 addWorker
添加一個線程來執行這個任務。否則通過 workQueue.offer(command)
方法入列。如果入列成功還需要在一次判斷池中的線程數,因爲我們創建線程池時可能要求核心線程數量爲0,所以我們必須使用 addWorker(null, false)
來創建一個臨時線程去阻塞隊列中獲取任務來執行。
isRunning( c )
的作用是判斷線程池是否處於運行狀態,如果入列後發現線程池已經關閉,則出列。不需要在入列前判斷線程池的狀態,因爲判斷一個線程池工作處於RUNNING狀態到執行入列操作這段時間,線程池可能被其它線程關閉了,所以提前判斷毫無意義。
1.3、addWorker源碼
下面代碼中用到的Worker類只是對Runnable的一個封裝,內部引用了Runnable和Thread,源碼解析可以參看這篇blog
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
// 獲取運行狀態
int rs = runStateOf(c);
/*
* 這個if判斷
* 如果rs >= SHUTDOWN,則表示此時不再接收新任務;
* 接着判斷以下3個條件,只要有1個不滿足,則返回false:
* 1. rs == SHUTDOWN,這時表示關閉狀態,不再接受新提交的任務,但卻可以繼續處理阻塞隊列中已保存的任務
* 2. firsTask爲空
* 3. 阻塞隊列不爲空
*
* 首先考慮rs == SHUTDOWN的情況
* 這種情況下不會接受新提交的任務,所以在firstTask不爲空的時候會返回false;
* 然後,如果firstTask爲空,並且workQueue也爲空,則返回false,
* 因爲隊列中已經沒有任務了,不需要再添加線程了
*/
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 獲取線程數
int wc = workerCountOf(c);
// 如果wc超過CAPACITY,也就是ctl的低29位的最大值(二進制是29個1),返回false;
// 這裏的core是addWorker方法的第二個參數,如果爲true表示根據corePoolSize來比較,
// 如果爲false則根據maximumPoolSize來比較。
//
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 嘗試增加workerCount,如果成功,則跳出第一個for循環
if (compareAndIncrementWorkerCount(c))
break retry;
// 如果增加workerCount失敗,則重新獲取ctl的值
c = ctl.get(); // Re-read ctl
// 如果當前的運行狀態不等於rs,說明狀態已被改變,返回第一個for循環繼續執行
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 根據firstTask來創建Worker對象
w = new Worker(firstTask);
// 每一個Worker對象都會創建一個線程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
// rs < SHUTDOWN表示是RUNNING狀態;
// 如果rs是RUNNING狀態或者rs是SHUTDOWN狀態並且firstTask爲null,向線程池中添加線程。
// 因爲在SHUTDOWN時不會在添加新的任務,但還是會執行workQueue中的任務
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// workers是一個HashSet
workers.add(w);
int s = workers.size();
// largestPoolSize記錄着線程池中出現過的最大線程數量
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 啓動線程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
2、線程的執行過程
2.1、runWorker源碼
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 獲取第一個任務
Runnable task = w.firstTask;
w.firstTask = null;
// 允許中斷
w.unlock(); // allow interrupts
// 是否因爲異常退出循環
boolean completedAbruptly = true;
try {
// 如果task爲空,則通過getTask來獲取任務
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);
}
}
這裏說明一下第一個if判斷,目的是:
- 如果線程池正在停止,那麼要保證當前線程是中斷狀態;
- 如果不是的話,則要保證當前線程不是中斷狀態;
總結一下runWorker方法的執行過程:
- while循環不斷地通過getTask()方法獲取任務;
- getTask()方法從阻塞隊列中取任務;
- 如果線程池正在停止,那麼要保證當前線程是中斷狀態,否則要保證當前線程不是中斷狀態;
- 調用task.run()執行任務;
- 如果task爲null則跳出循環,執行processWorkerExit()方法;
- runWorker方法執行完畢,也代表着Worker中的run方法執行完畢,銷燬線程。
這裏的beforeExecute方法和afterExecute方法在ThreadPoolExecutor類中是空的,留給子類來實現。
completedAbruptly 變量來表示在執行任務過程中是否出現了異常,在processWorkerExit 方法中會對該變量的值進行判斷。
Thread的run方法實際上調用了 Worker 類的 runWorker
方法,而 Worker
類繼承了 AQS
類,並實現了 lock
、unlock
、trylock
方法。但是這些方法不是真正意義上的鎖,所以在代碼中加鎖操作和解鎖操作沒有成對出現。
runWorker
方法中獲取到任務就“加鎖”,完成任務後就“解鎖”。也就是說在“加鎖”到“解鎖”的這段時間內,線程處於忙碌狀態,而其它時間段,處於空閒狀態。線程池就可以通過 trylock
方法來確定這個線程是否空閒。
getTask
方法的主要作用是從阻塞隊列中獲取任務。
beforeExecute(wt, task)
和 afterExecute(task, thrown)
是個鉤子函數,如果我們需要在任務執行之前和任務執行以後進行一些操作,那麼我們可以自定義一個繼承 ThreadPoolExecutor 類,並覆蓋這兩個方法。
2.2、getTask 源代碼
private Runnable getTask() {
// timeOut變量的值表示上次從阻塞隊列中取任務時是否超時
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.
/*
* 如果線程池狀態rs >= SHUTDOWN,也就是非RUNNING狀態,再進行以下判斷:
* 1. rs >= STOP,線程池是否正在stop;
* 2. 阻塞隊列是否爲空。
* 如果以上條件滿足,則將workerCount減1並返回null。
* 因爲如果當前線程池狀態的值是SHUTDOWN或以上時,不允許再向阻塞隊列中添加任務。
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
// timed變量用於判斷是否需要進行超時控制。
// allowCoreThreadTimeOut默認是false,也就是核心線程不允許進行超時;
// wc > corePoolSize,表示當前線程池中的線程數量大於核心線程數量;
// 對於超過核心線程數量的這些線程,需要進行超時控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/*
* wc > maximumPoolSize的情況是因爲可能在此方法執行階段同時執行了setMaximumPoolSize方法;
* timed && timedOut 如果爲true,表示當前操作需要進行超時控制,並且上次從阻塞隊列中獲取任務發生了超時
* 接下來判斷,如果有效線程數量大於1,或者阻塞隊列是空的,那麼嘗試將workerCount減1;
* 如果減1失敗,則返回重試。
* 如果wc == 1時,也就說明當前線程是線程池中唯一的一個線程了。
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
/*
* 根據timed來判斷,如果爲true,則通過阻塞隊列的poll方法進行超時控制,如果在keepAliveTime時間內沒有獲取到任務,則返回null;
* 否則通過take方法,如果這時隊列爲空,則take方法會阻塞直到隊列不爲空。
*
*/
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
// 如果 r == null,說明已經超時,timedOut設置爲true
timedOut = true;
} catch (InterruptedException retry) {
// 如果獲取任務時當前線程發生了中斷,則設置timedOut爲false並返回循環重試
timedOut = false;
}
}
}
可以看出如果允許線程在keepAliveTime時間內未獲取到任務線程就銷燬就調用workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),否則會調用workQueue.take()方法(該方法即使獲取不到任務就會一直阻塞下去)。而確定是否使用workQueue.poll方法只有兩個條件決定,一個是當前池中的線程是否大於核心線程數量,第二個是是否允許核心線程銷燬,兩者其一滿足就會調用該方法。
3、 線程池的關閉過程
3.1、shutdown源碼
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown();// hook for ScheduledThreadPoolExecutor
}finally {
mainLock.unlock();
}
tryTerminate();
}
advanceRunState(SHUTDOWN) 的作用是通過CAS操作將線程池的狀態更改爲 SHUTDOWN 狀態。
interruptIdleWorkers 是對空閒的線程進行中斷,它實際上調用了重載帶參數的函數 interruptIdleWorkers(false)
onShutdown 也是一個鉤子函數
3.2、interruptIdleWorkers 源碼
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();
}
}
通過workers容器,遍歷池中的線程,對每個線程進行tryLock()操作,如果成功說明線程空閒,則設置其中斷標誌位。而線程是否響應中斷則由任務的編寫者決定。