如何優雅的關閉Java執行緒池
⾯試中經常會問到,建立⼀個執行緒池需要哪些引數、執行緒池的工作原理,卻很少會問到執行緒池如何安全關閉的。
也正是因為⼤家不是很關注這塊,即便是⼯作三四年的⼈,也會有因為執行緒池關閉不合理,導致應用⽆法正常stop的情況,還有出現⼀些報錯的問題。
本篇就以ThreadPoolExecutor為例例,來介紹下如何優雅的關閉執行緒池。
執行緒中斷
在介紹執行緒池關閉之前,先介紹下Thread的interrupt。
在程式中,我們是不能隨便中斷⼀個執行緒的,因為這是極其不安全的操作,我們⽆法知道這個執行緒正運⾏在什麼狀態,它可能持有某把鎖,強⾏中斷可能導致鎖不能釋放的問題;或者執行緒可能在運算元據庫,強⾏中斷導致資料不一致,從而混亂的問題。正因此,Java⾥將Thread的stop⽅法設定為過時,以禁⽌⼤家使⽤。
⼀個執行緒什麼時候可以退出呢?當然只有執行緒自⼰才能知道。
所以我們這⾥要說的Thread的interrrupt⽅法,本質不是⽤來中斷一個執行緒。而是將執行緒設定⼀箇中斷狀態。當我們調⽤執行緒的interrupt方法,它有兩個作⽤用:
1、如果此執行緒處於阻塞狀態(比如調⽤了wait方法,io等待),則會立刻退出阻塞,並丟擲InterruptedException異常,執行緒就可以通過捕獲InterruptedException來做⼀定的處理,然後讓執行緒退出。
2、如果此執行緒正處於運行之中,則執行緒不受任何影響,繼續運行,僅僅是執行緒的中斷標記被設定為true。所以執行緒要在適當的位置通過呼叫isInterrupted方法來檢視自⼰是否被中斷,並做退出操作。
注:如果執行緒的interrupt方法先被呼叫,然後執行緒呼叫阻塞方法進入阻塞狀態,InterruptedException異常依舊會丟擲。如果執行緒捕獲InterruptedException異常後,繼續呼叫阻塞方法, 將不再觸發InterruptedException異常。
執行緒池的關閉
執行緒池提供了兩個關閉方法,shutdownNow和shuwdown⽅法。
shutdownNow⽅法的解釋是:執行緒池拒接收新提交的任務,同時⽴刻關閉執行緒池,執行緒池里的任務不再執行。
shutdown⽅法的解釋是:執行緒池拒接收新提交的任務,同時等待執行緒池⾥的任務執行完畢後關閉執行緒池。
以上的說法雖然沒錯,但是還有很多的細節問題,比如呼叫shutdown⽅法後,正在執⾏的任務的執行緒會做出什麼反應?正在等待任務的執行緒又會做出什麼反應?執行緒在什麼情況下才會徹底退出。如果不瞭解這些細節,在關閉執行緒池時就難免遇到,像執行緒池關閉不了,關閉執行緒池出現報錯等情況。
再說這些關閉執行緒池細節問題之前,需要強調一點的是,呼叫完shutdownNow和shuwdown⽅法後,並不代表執行緒池已經完成關閉操作,它只是非同步的通知執行緒池進行關閉處理。如果要同步等待執行緒池徹底關閉後才繼續往下執行,需要調⽤awaitTermination⽅法進⾏同步等待。
有了以上介紹,下⾯就結合執行緒池原始碼,分別說說這兩個執行緒池關閉方法的⼀一些實現細節。
shutdownNow
我們看⼀下shutdownNow⽅法的原始碼:
在shutdownNow⽅法里,重要的三句程式碼我⽤紅⾊數字標出來了。第⼀句就是原⼦性的修改執行緒池的狀態為STOP狀態(比較簡單,我就不貼程式碼了) ,第三句是將隊列里還沒有執⾏的任務放到列表里,返回給呼叫方,第⼆句是遍歷執行緒池⾥的所有⼯作執行緒,然後呼叫執行緒的interrupt方法。如下圖:
以上就是shutdownNow⽅法的執⾏邏輯:將執行緒池狀態修改為STOP,然後調⽤執行緒池⾥的所有執行緒的interrupt⽅法。
調⽤shutdownNow後,執行緒池⾥的執行緒會做如何反應呢?那就要看,執行緒池⾥的執行緒正在執⾏的程式碼邏輯了。其線上程池的runWorker⽅法里(對執行緒池的執行原理不了解的,請看之前的文章),其程式碼如下:
正常情況下,執行緒池里的執行緒,就是在這個while迴圈里不停地執行。其中程式碼task.run()就是在執⾏我們提交給執行緒池的任務,如當我們呼叫shutdownNow 時,task.run()⾥⾯正處於IO阻塞,則會導致報錯,如果task.run()⾥正在正常執
⾏,則不受影響,繼續執⾏完這個任務。
從上圖看得出來,如果getTask()⽅法返回null,也會導致執行緒的退出。我們再來看看getTask⽅法的實現:
如果我們調⽤shutdownNow⽅法時,執行緒處於從佇列⾥讀取任務⽽阻塞中(圖中下邊的紅框),則會導致丟擲InterruptedException異常,但因為異常被捕獲,執行緒將會繼續在這個for迴圈⾥執⾏。
還記得shutdownNow⽅法里將執行緒修改為STOP狀態吧,當執行到上邊紅框⾥的程式碼時,由於STOP狀態值是⼤於SHUTDOWN狀態,STOP也大於等於STOP,不管任務佇列是否為空,都會進⼊if語句從而返回null,執行緒退出。
總結:當我們呼叫執行緒池的shutdownNow時,如果執行緒正在getTask方法中執⾏,則會通過for迴圈進入到if語句,於是getTask 返回null,從而執行緒退出。不管執行緒池⾥是否有未完成的任務。如果執行緒因為執行提交到執行緒池里的任務而處於阻塞狀態,則會導致報錯。(如果任務里沒有捕獲InterruptedException異常),否則執行緒會執行完當前任務,然後通過getTask方法返回為null來退出。
shutdown
我們再來看看shutdown⽅法的原始碼:
跟shutdownNow類似,只不過它是將執行緒池的狀態修改為SHUTDOWN狀態,然後調⽤interruptIdleWorkers方法,來中斷空閒的執行緒。這是interruptIdleWorkers⽅法的實現:
跟shutdownNow⽅法調⽤interruptWorkers⽅法不同的是,interruptIdleWorkers⽅法在遍歷執行緒池⾥的執行緒時,有一個w.tryLock()加鎖判斷,只有加鎖成功的執行緒才會被呼叫interrupt方法。那麼什麼情況下才能被加鎖成功?什麼情況下不能被加鎖成功呢?這就需要我們繼續回到執行緒執行的runWorker方法。
在上邊runWorker方法程式碼的截圖中,我刻意將w.lock()和w.unlock()呼叫用紅框 圈起。其實就是正運行在w.lock和w.unlock之間的執行緒將因為加鎖失敗,而不會被呼叫interrupt方法,換句話說,就是正在執行執行緒池裡任務的執行緒不會被中斷。
不管是被呼叫了interrupt的執行緒還是沒被呼叫的執行緒,什麼時候退出呢?,這就要看getTask⽅法的返回是否為null了。
在getTask里的if判斷(上文中getTask程式碼截圖中上邊紅色方框的程式碼)中,由於執行緒池被shutdown⽅法修改為SHUTDOWN狀態,SHUTDOWN大於等於
SHUTDOWN成⽴沒問題,但是SHUTDOWN不⼤於等於STOP狀態,所以只有佇列為空,getTask方法才會返回null,導致執行緒退出。
總結:當我們呼叫執行緒池的shuwdown方法時,如果執行緒正在執行執行緒池里的任務,即便任務處於阻塞狀態,執行緒也不會被中斷,⽽是繼續執行。如果執行緒池阻塞等待從隊列⾥讀取任務,則會被喚醒,但是會繼續判斷佇列是否為空,若不為空,則會繼續從隊列里讀取任務,若為空則執行緒退出。
優雅的關閉執行緒池
有了上邊對兩個關閉執行緒池方法的了解,相信優雅安全關閉執行緒池將不再是問題。
我們知道,使⽤shutdownNow⽅法,可能會引起報錯,使用shutdown方法可能會導致執行緒關閉不了。
所以當我們使⽤shutdownNow⽅法關閉執行緒池時,一定要對任務里進行異常捕獲。
當我們使用shuwdown方法關閉執行緒池時,一定要確保任務裡不會有永久阻塞等待的邏輯,否則執行緒池就關閉不了。
最後,⼀定要記得shutdownNow和shuwdown呼叫完,執行緒池並不是⽴刻就關閉了,要想等待執行緒池關閉,還需呼叫awaitTermination⽅法來阻塞等待。
END
歡迎工作一到五年的Java程式設計師朋友們加入Java架構開發:744677563
本群提供免費的學習指導 架構資料 以及免費的解答
不懂得問題都可以在本群提出來 之後還會有職業生涯規劃以及面試指導
相關文章
- Java優雅關閉執行緒池Java執行緒
- 優雅關閉執行緒池的方案執行緒
- 如何優雅的使用執行緒池執行緒
- 如何優雅的使用和理解執行緒池執行緒
- Spring Boot使用@Async實現非同步呼叫:ThreadPoolTaskScheduler執行緒池的優雅關閉Spring Boot非同步thread執行緒
- 執行緒池關閉的小結執行緒
- 面試官:說一說如何優雅的關閉執行緒池,我:shutdownNow,面試官:粗魯!面試執行緒
- 詳解執行緒池的作用及Java中如何使用執行緒池執行緒Java
- 如何優雅的停止一個執行緒?執行緒
- Java執行緒池二:執行緒池原理Java執行緒
- python多執行緒中:如何關閉執行緒?Python執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- Java執行緒池Java執行緒
- java 執行緒池Java執行緒
- 原始碼|從序列執行緒封閉到物件池、執行緒池原始碼執行緒物件
- SpringBoot執行緒池和Java執行緒池的實現原理Spring Boot執行緒Java
- java執行緒池趣味事:這不是執行緒池Java執行緒
- java多執行緒9:執行緒池Java執行緒
- JAVA執行緒池的使用Java執行緒
- java--執行緒池--建立執行緒池的幾種方式與執行緒池操作詳解Java執行緒
- 執行緒池相關執行緒
- 搞懂Java執行緒池Java執行緒
- Java執行緒池一:執行緒基礎Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- 如何"優雅"地終止一個執行緒?執行緒
- 如何實現線上優雅停機和調整執行緒池引數?執行緒
- 如何優雅的關閉Go Channel「譯」Go
- 安全優雅地停止執行緒執行緒
- Java併發 之 執行緒池系列 (1) 讓多執行緒不再坑爹的執行緒池Java執行緒
- 走進Java Android 的執行緒世界(二)執行緒池JavaAndroid執行緒
- Java執行緒池詳解Java執行緒
- Java執行緒池進階Java執行緒
- java執行緒池實踐Java執行緒
- Java 執行緒池詳解Java執行緒
- Java執行緒池歸納Java執行緒
- Java執行緒池之ThreadPoolExecutorJava執行緒thread
- java-執行緒池(一)Java執行緒
- 速讀Java執行緒池Java執行緒