線程池關閉的方式
使用10個固定線程池創建100個任務
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
service.execute(new Task());
}
ThreadPoolExecutor中關閉線程池的方法
- shutdown()
- shutdownNow()
- isShutdown()
- isTerminated()
- awaitTermination()
shutdown
可以安全地關閉一個線程池,調用 shutdown() 方法之后線程池並不是立刻就被關閉,因為這時線程池中可能還有很多任務正在被執行,或是任務隊列中有大量正在等待被執行的任務,調用 shutdown() 方法后線程池會在執行完正在執行的任務和隊列中等待的任務后才徹底關閉。但這並不代表 shutdown() 操作是沒有任何效果的,調用 shutdown() 方法后如果還有新的任務被提交,線程池則會根據拒絕策略直接拒絕后續新提交的任務。
isShutdown
可以返回 true 或者 false 來判斷線程池是否已經開始了關閉工作,也就是是否執行了 shutdown 或者 shutdownNow 方法。這里需要注意,如果調用 isShutdown() 方法的返回的結果為 true 並不代表線程池此時已經徹底關閉了,這僅僅代表線程池開始了關閉的流程,也就是說,此時可能線程池中依然有線程在執行任務,隊列里也可能有等待被執行的任務。
isTerminated
檢測線程池是否真正“終結”了,這不僅代表線程池已關閉,同時代表線程池中的所有任務都已經都執行完畢了,因為我們剛才說過,調用 shutdown 方法之后,線程池會繼續執行里面未完成的任務,不僅包括線程正在執行的任務,還包括正在任務隊列中等待的任務。比如此時已經調用了 shutdown 方法,但是有一個線程依然在執行任務,那么此時調用 isShutdown 方法返回的是 true ,而調用 isTerminated 方法返回的便是 false ,因為線程池中還有任務正在在被執行,線程池並沒有真正“終結”。直到所有任務都執行完畢了,調用 isTerminated() 方法才會返回 true,這表示線程池已關閉並且線程池內部是空的,所有剩余的任務都執行完畢了。
awaitTermination
主要用來判斷線程池狀態的。比如我們給 awaitTermination 方法傳入的參數是 10 秒,那么它就會陷入 10 秒鍾的等待,直到發生以下三種情況之一:
- 等待期間(包括進入等待狀態之前)線程池已關閉並且所有已提交的任務(包括正在執行的和隊列中等待的)都執行完畢,相當於線程池已經“終結”了,方法便會返回 true;
- 等待超時時間到后,第一種線程池“終結”的情況始終未發生,方法返回 false;
- 等待期間線程被中斷,方法會拋出 InterruptedException 異常。
shutdownNow
功能最強大,它與第一種 shutdown 方法不同之處在於名字中多了一個單詞 Now,也就是表示立刻關閉的意思。在執行 shutdownNow 方法之后,首先會給所有線程池中的線程發送 interrupt 中斷信號,嘗試中斷這些任務的執行,然后會將任務隊列中正在等待的所有任務轉移到一個 List 中並返回,我們可以根據返回的任務 List 來進行一些補救的操作,例如記錄在案並在后期重試。shutdownNow() 的源碼如下所示。
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
你可以看到源碼中有一行 interruptWorkers() 代碼,這行代碼會讓每一個已經啟動的線程都中斷,這樣線程就可以在執行任務期間檢測到中斷信號並進行相應的處理,提前結束任務。這里需要注意的是,由於 Java 中不推薦強行停止線程的機制的限制,即便我們調用了 shutdownNow 方法,如果被中斷的線程對於中斷信號不理不睬,那么依然有可能導致任務不會停止。可見我們在開發中落地最佳實踐是很重要的,我們自己編寫的線程應當具有響應中斷信號的能力,正確停止線程的方法在第 2 講有講過,應當利用中斷信號來協同工作。