關(guān)閉線程池我們可以選擇什么都不做,JVM 關(guān)閉時自然的會清除線程池對象。當(dāng)然這么做,存在很大的弊端,線程池中正在執(zhí)行執(zhí)行的線程以及隊列中還未執(zhí)行任務(wù)將會變得極不可控。所以我們需要想辦法控制到這些未執(zhí)行的任務(wù)以及正在執(zhí)行的線程。
線程池 API 提供兩個主動關(guān)閉的方
法 ThreadPoolExecutor#shutdownNow 與 ThreadPoolExecutor#shutdown,這兩個方法都可以用于關(guān)閉線程池,但是具體效果卻不太一樣。
一、線程池的狀態(tài)
在說線程池關(guān)閉方法之前,我們先了解線程池狀態(tài)。
線程池狀態(tài)關(guān)系圖如下:
從上圖我們看到線程池總共存在 5 種狀態(tài),分別為:
- RUNNING:線程池創(chuàng)建之后的初始狀態(tài),這種狀態(tài)下可以執(zhí)行任務(wù)。
- SHUTDOWN:該狀態(tài)下線程池不再接受新任務(wù),但是會將工作隊列中的任務(wù)執(zhí)行結(jié)束。
- STOP: 該狀態(tài)下線程池不再接受新任務(wù),但是不會處理工作隊列中的任務(wù),并且將會中斷線程。
- TIDYING:該狀態(tài)下所有任務(wù)都已終止,將會執(zhí)行 terminated() 鉤子方法。
- TERMINATED:執(zhí)行完 terminated() 鉤子方法之后。
當(dāng)我們執(zhí)行 ThreadPoolExecutor#shutdown 方法將會使線程池狀態(tài)從 RUNNING 轉(zhuǎn)變?yōu)?nbsp;SHUTDOWN。而調(diào)用 ThreadPoolExecutor#shutdownNow 之后線程池狀態(tài)將會從 RUNNING 轉(zhuǎn)變?yōu)?nbsp;STOP。從上面的圖上還可以看到,當(dāng)線程池處于 SHUTDOWN,我們還是可以繼續(xù)調(diào)用 ThreadPoolExecutor#shutdownNow 方法,將其狀態(tài)轉(zhuǎn)變?yōu)?nbsp;STOP 。
二、ThreadPoolExecutor#shutdown
上面我們知道線程池狀態(tài),這里先說說 shutdown 方法。shutdown 方法源碼比較簡單,能比較直觀理解其調(diào)用邏輯。
shutdown 方法源碼:
shutdown 方法首先加鎖,其次先檢查系統(tǒng)安裝狀態(tài)。接著就會將線程池狀態(tài)變?yōu)?nbsp;SHUTDOWN,在這之后線程池不再接受提交的新任務(wù)。此時如果還繼續(xù)往線程池提交任務(wù),將會使用線程池拒絕策略響應(yīng),默認(rèn)情況下將會使用 ThreadPoolExecutor.AbortPolicy,拋出 RejectedExecutionException 異常。
interruptIdleWorkers 方法只會中斷空閑的線程,不會中斷正在執(zhí)行任務(wù)的的線程。空閑的線程將會阻塞在線程池的阻塞隊列上。
線程池構(gòu)造參數(shù)需要指定 coreSize(核心線程池數(shù)量),maximumPoolSize(最大的線程池數(shù)量),keepAliveTime(多余空閑線程等待時間),unit(時間單位),workQueue(阻塞隊列)。
當(dāng)調(diào)用線程池的 execute 方法,線程池工作流程如下:
- 如果此時線程池中線程數(shù)量小于 coreSize,將會新建線程執(zhí)行提交的任務(wù)。
- 如果此時線程池線程數(shù)量已經(jīng)大于 coreSize,將會直接把任務(wù)加入到隊列中。線程將會從工作隊列中獲取任務(wù)執(zhí)行。
- 如果工作隊列已滿,將會繼續(xù)新建線程。
- 如果工作隊列已滿,且線程數(shù)等于 maximumPoolSize,此時將會使用拒絕策略拒絕任務(wù)。
- 超過 coreSize 數(shù)量那部分線程,如果空閑了 keepAliveTime ,線程將會終止。
工作流程圖如下:
當(dāng)線程池處于第二步時,線程將會使用 workQueue#take 獲取隊頭的任務(wù),然后完成任務(wù)。如果工作隊列一直沒任務(wù),由于隊列為阻塞隊列,workQueue#take 將會阻塞線程。
三、ThreadPoolExecutor#shutdownNow
ThreadPoolExecutor#shutdownNow 源碼如下:
shutdownNow 方法將會把線程池狀態(tài)設(shè)置為 STOP,然后中斷所有線程,最后取出工作隊列中所有未完成的任務(wù)返回給調(diào)用者。
對比 shutdown 方法,shutdownNow 方法比較粗暴,直接中斷工作線程。不過這里需要注意,中斷線程并不代表線程立刻結(jié)束。這里需要線程主動配合線程中斷響應(yīng)。
線程中斷機制: thread#interrupt 只是設(shè)置一個中斷標(biāo)志,不會立即中斷正常的線程。如果想讓中斷立即生效,必須在線程 內(nèi)調(diào)用 Thread.interrupted() 判斷線程的中斷狀態(tài)。 對于阻塞的線程,調(diào)用中斷時,線程將會立刻退出阻塞狀態(tài)并拋出 InterruptedException 異常。所以對于阻塞線程需要正確處理 InterruptedException 異常。
awaitTermination
線程池 shutdown 與 shutdownNow 方法都不會主動等待執(zhí)行任務(wù)的結(jié)束,如果需要等到線程池任務(wù)執(zhí)行結(jié)束,需要調(diào)用 awaitTermination 主動等待任務(wù)調(diào)用結(jié)束。
調(diào)用方法如下:
如果線程池任務(wù)執(zhí)行結(jié)束,awaitTermination 方法將會返回 true,否則當(dāng)?shù)却龝r間超過指定時間后將會返回 false。
如果需要使用這種進(jìn)制,建議在上面的基礎(chǔ)上增加一定重試次數(shù)。這個真的很重要!!!
四、優(yōu)雅關(guān)閉線程池
回顧上面線程池狀態(tài)關(guān)系圖,我們可以知道處于 SHUTDOWN 的狀態(tài)下的線程池依舊可以調(diào)用 shutdownNow。所以我們可以結(jié)合 shutdown , shutdownNow,awaitTermination ,更加優(yōu)雅關(guān)閉線程池。






