大家好,我是冰河~~
在【高並行專題】中,我們從原始碼角度深度分析了執行緒池中那些重要的介面和抽象類、深度解析了執行緒池是如何建立的,ThreadPoolExecutor類有哪些屬性和內部類,以及它們對執行緒池的重要作用。深度分析了執行緒池的整體核心流程,以及如何拆解Worker執行緒的執行程式碼,深度解析Worker執行緒的執行流程。
本文,我們就來從原始碼角度深度解析執行緒池是如何優雅的退出程式的。首先,我們來看下ThreadPoolExecutor類中的shutdown()方法。
當使用執行緒池的時候,呼叫了shutdown()方法後,執行緒池就不會再接受新的執行任務了。但是在呼叫shutdown()方法之前放入任務佇列中的任務還是要執行的。此方法是非阻塞方法,呼叫後會立即返回,並不會等待任務佇列中的任務全部執行完畢後再返回。我們看下shutdown()方法的原始碼,如下所示。
public void shutdown() {
//獲取執行緒池的全域性鎖
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//檢查是否有關閉執行緒池的許可權
checkShutdownAccess();
//將當前執行緒池的狀態設定為SHUTDOWN
advanceRunState(SHUTDOWN);
//中斷Worker執行緒
interruptIdleWorkers();
//為ScheduledThreadPoolExecutor呼叫勾點函數
onShutdown(); // hook for
} finally {
//釋放執行緒池的全域性鎖
mainLock.unlock();
}
//嘗試將狀態變為TERMINATED
tryTerminate();
}
總體來說,shutdown()方法的程式碼比較簡單,首先檢查了是否有許可權來關閉執行緒池,如果有許可權,則再次檢測是否有中斷工作執行緒的許可權,如果沒有許可權,則會丟擲SecurityException異常,程式碼如下所示。
//檢查是否有關閉執行緒池的許可權
checkShutdownAccess();
//將當前執行緒池的狀態設定為SHUTDOWN
advanceRunState(SHUTDOWN);
//中斷Worker執行緒
interruptIdleWorkers();
其中,checkShutdownAccess()方法的實現程式碼如下所示。
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();
}
}
}
對於checkShutdownAccess()方法的程式碼理解起來比較簡單,就是檢測是否具有關閉執行緒池的許可權,期間使用了執行緒池的全域性鎖。
接下來,我們看advanceRunState(int)方法的原始碼,如下所示。
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
advanceRunState(int)方法的整體邏輯就是:判斷當前執行緒池的狀態是否為指定的狀態,在shutdown()方法中傳遞的狀態是SHUTDOWN,如果是SHUTDOWN,則直接返回;如果不是SHUTDOWN,則將當前執行緒池的狀態設定為SHUTDOWN。
接下來,我們看看showdown()方法呼叫的interruptIdleWorkers()方法,如下所示。
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
可以看到,interruptIdleWorkers()方法呼叫的是interruptIdleWorkers(boolean)方法,繼續看interruptIdleWorkers(boolean)方法的原始碼,如下所示。
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();
}
}
上述程式碼的總體邏輯為:獲取執行緒池的全域性鎖,迴圈所有的工作執行緒,檢測執行緒是否被中斷,如果沒有被中斷,並且Worker執行緒獲得了鎖,則執行執行緒的中斷方法,並釋放執行緒獲取到的鎖。此時如果onlyOne引數為true,則退出迴圈。否則,迴圈所有的工作執行緒,執行相同的操作。最終,釋放執行緒池的全域性鎖。
接下來,我們看下shutdownNow()方法。
如果呼叫了執行緒池的shutdownNow()方法,則執行緒池不會再接受新的執行任務,也會將任務佇列中存在的任務丟棄,正在執行的Worker執行緒也會被立即中斷,同時,方法會立刻返回,此方法存在一個返回值,也就是當前任務佇列中被丟棄的任務列表。
shutdownNow()方法的原始碼如下所示。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//檢查是否有關閉許可權
checkShutdownAccess();
//設定執行緒池的狀態為STOP
advanceRunState(STOP);
//中斷所有的Worker執行緒
interruptWorkers();
//將任務佇列中的任務移動到tasks集合中
tasks = drainQueue();
} finally {
mainLock.unlock();
}
/嘗試將狀態變為TERMINATED
tryTerminate();
//返回tasks集合
return tasks;
}
shutdownNow()方法的原始碼的總體邏輯與shutdown()方法基本相同,只是shutdownNow()方法將執行緒池的狀態設定為STOP,中斷所有的Worker執行緒,並且將任務佇列中的所有任務移動到tasks集合中並返回。
可以看到,shutdownNow()方法中斷所有的執行緒時,呼叫了interruptWorkers()方法,接下來,我們就看下interruptWorkers()方法的原始碼,如下所示。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
interruptWorkers()方法的邏輯比較簡單,就是獲得執行緒池的全域性鎖,迴圈所有的工作執行緒,依次中斷執行緒,最後釋放執行緒池的全域性鎖。
在interruptWorkers()方法的內部,實際上呼叫的是Worker類的interruptIfStarted()方法來中斷執行緒,我們看下Worker類的interruptIfStarted()方法的原始碼,如下所示。
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
發現其本質上呼叫的還是Thread類的interrupt()方法來中斷執行緒。
當執行緒池呼叫了awaitTermination(long, TimeUnit)方法後,會阻塞呼叫者所在的執行緒,直到執行緒池的狀態修改為TERMINATED才返回,或者達到了超時時間返回。接下來,我們看下awaitTermination(long, TimeUnit)方法的原始碼,如下所示。
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
//獲取距離超時時間剩餘的時長
long nanos = unit.toNanos(timeout);
//獲取Worker執行緒的的全域性鎖
final ReentrantLock mainLock = this.mainLock;
//加鎖
mainLock.lock();
try {
for (;;) {
//當前執行緒池狀態為TERMINATED狀態,會返回true
if (runStateAtLeast(ctl.get(), TERMINATED))
return true;
//達到超時時間,已超時,則返回false
if (nanos <= 0)
return false;
//重置距離超時時間的剩餘時長
nanos = termination.awaitNanos(nanos);
}
} finally {
//釋放鎖
mainLock.unlock();
}
}
上述程式碼的總體邏輯為:首先獲取Worker執行緒的獨佔鎖,後在迴圈判斷當前執行緒池是否已經是TERMINATED狀態,如果是則直接返回true,否則檢測是否已經超時,如果已經超時,則返回false。如果未超時,則重置距離超時時間的剩餘時長。接下來,進入下一輪迴圈,再次檢測當前執行緒池是否已經是TERMINATED狀態,如果是則直接返回true,否則檢測是否已經超時,如果已經超時,則返回false。如果未超時,則重置距離超時時間的剩餘時長。以此迴圈,直到執行緒池的狀態變為TERMINATED或者已經超時。
好了,今天就到這兒吧,我是冰河,我們下期見~~