前面提到ExecutorService是對Executor的擴充套件,增加了submit()等提交和呼叫任務的方法,也增加了對Executor生命週期的定義。本篇將結合shutdown()、shutdownNow()、awaitTermination()和tryTerminate()幾個方法的實現對ThreadPoolExecutor生命週期相關的邏輯做分析整理。
0. ExecutorService的生命週期方法和ThreadPoolExecutor的生命週期實現
ExecutorService中,和生命週期相關的,宣告瞭5個方法:
- awaitTermination() 阻塞等待shutdown請求後所有執行緒終止,會有時間引數,超時和中斷也會令方法呼叫結束
- isShutdown() 通過ctl屬性判斷當前的狀態是否不是RUNNING狀態
- isTerminated() 通過ctl屬性判斷當前的狀態是否為TERMINATED狀態
- shutdown() 關閉Executor,不再接受提交任務
- shutdownNow() 關閉Executor,不再接受提交任務,並且不再執行入佇列中的任務
前面的文章也已經介紹過,在ThreadPoolExecutor的實現中,定義了五個生命週期狀態,標識了整個執行緒池物件所處的狀態階段,用來實現生命週期相關的方法。
1. ThreadPoolExecutor的shutdown()
我們先來看看shutdown()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
public
void
shutdown() { final
ReentrantLock mainLock = this .mainLock; mainLock.lock(); try
{ checkShutdownAccess(); advanceRunState(SHUTDOWN); interruptIdleWorkers(); onShutdown();
//
hook for ScheduledThreadPoolExecutor }
finally
{ mainLock.unlock(); } tryTerminate(); } |
其實邏輯比較簡單,嘗試將狀態切換到SHUTDOWN,這樣就不會再接收新的任務提交。對空閒執行緒進行中斷呼叫。最後檢查執行緒池執行緒是否為0,並嘗試切換到TERMINATED狀態。
2. ThreadPoolExecutor的shutdownNow()
再來看看shutdownNow()方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
public
List shutdownNow() { List
tasks; final
ReentrantLock mainLock = this .mainLock; mainLock.lock(); try
{ checkShutdownAccess(); advanceRunState(STOP); interruptWorkers(); tasks
= drainQueue(); }
finally
{ mainLock.unlock(); } tryTerminate(); return
tasks; } |
主要所做的事情就是切換ThreadPoolExecutor到STOP狀態,中斷所有worker,並將任務佇列中的任務取出來,不再執行。最後嘗試修改狀態到TERMINATED。
3. shutdown()和shutdownNow()的區別
shutdown()新的任務不會再被提交到執行緒池,但之前的都會依舊執行,通過中斷方式停止空閒的(根據沒有獲取鎖來確定)執行緒。
shutdownNow()則向所有正在執行的執行緒發出中斷訊號以嘗試終止執行緒,並將工作佇列中的任務以列表方式的結果返回。
兩者區別:
- 是一個要將執行緒池推到SHUTDOWN狀態,一個將推到STOP狀態
- 並且對執行的執行緒處理方式不同,shutdown()只中斷空閒執行緒,而shutdownNow()會嘗試中斷所有活動執行緒
- 還有就是對佇列中的任務處理,shutdown()佇列中已有任務會繼續執行,而shutdownNow()會直接取出不被執行
相同的是都在最後嘗試將執行緒池推到TERMINATED狀態。
4. ThreadPoolExecutor的awaitTermination()
阻塞等待shutdown請求後所有執行緒終止,會有時間引數,超時和中斷也會令方法呼叫結束。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public
boolean
awaitTermination( long
timeout, TimeUnit unit) throws
InterruptedException { long
nanos = unit.toNanos(timeout); final
ReentrantLock mainLock = this .mainLock; mainLock.lock(); try
{ for
(;;) { if
(runStateAtLeast(ctl.get(), TERMINATED)) return
true ; if
(nanos <= 0 ) return
false ; nanos
= termination.awaitNanos(nanos); } }
finally
{ mainLock.unlock(); } } |
實際所做的就是Condition的定時await呼叫。用於狀態依賴的執行緒阻塞。
5. tryTerminate()
tryTerminate()的意義就在於嘗試進入終止狀態,當ctl中worker數字為0時執行terminated()方法,否則等鎖中斷一個空閒的Worker。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
final
void
tryTerminate() { for
(;;) { int
c = ctl.get(); if
(isRunning(c) || runStateAtLeast(c,
TIDYING) || (runStateOf(c)
== SHUTDOWN && ! workQueue.isEmpty())) return ; if
(workerCountOf(c) != 0 )
{ //
Eligible to terminate interruptIdleWorkers(ONLY_ONE); return ; } final
ReentrantLock mainLock = this .mainLock; mainLock.lock(); try
{ if
(ctl.compareAndSet(c, ctlOf(TIDYING, 0 )))
{ try
{ terminated(); }
finally
{ ctl.set(ctlOf(TERMINATED,
0 )); termination.signalAll(); } return ; } }
finally
{ mainLock.unlock(); } //
else retry on failed CAS } } |
其中interruptIdleWorkers()方法這裡就不列程式碼了,空閒的worker主要是通過worker的tryLock()來確認的,因為執行任務的worker互斥地鎖定物件。
中斷worker導致執行緒退出,最終還會迴圈嘗試終止其它的空閒執行緒,直到整個ThreadPoolExecutor最後終結。
6. ThreadPoolExecutor生命週期的擴充套件點
在生命週期上,ThreadPoolExecutor為擴充套件的類提供了一些擴充套件點,這是很好的設計,對擴充套件開放。
其中宣告瞭如下protected的方法:
- beforeExecute() 在每個任務執行前做的處理
- afterExecute() 在每個任務執行後做的處理
- terminated() 在ThreadPoolExecutor到達TERMINATED狀態前所做的處理
- finalize() 有預設實現,直接呼叫shutdown(),以保證執行緒池物件回收
- onShutdown() 在shutdown()方法執行到最後時呼叫,在java.util.concurrent.ScheduledThreadPoolExecutor類實現中用到了這個擴充套件點,做一些任務佇列的清理操作。