InterruptedException和中断线程的解释
如果沒有將InterruptedException檢查為異常,則可能甚至沒人會注意到它–實際上,這些年來可以防止出現幾個錯誤。 但是由于必須對其進行處理,因此許多人不正確或不加考慮地處理它。 讓我們以一個線程的簡單示例為例,該線程定期進行一些清理,但大多數情況下在兩次睡眠之間進行。
此代碼在很多層上都是錯誤的!
所有問題都是有效的,但是吞沒InterruptedException是其最大的罪過。 在我們理解原因之前,讓我們先思考一下該異常的含義以及如何利用它來優雅地中斷線程。 JDK中的許多阻止操作都聲明拋出InterruptedException ,包括:
- Object.wait()
- Thread.sleep()
- Process.waitFor()
- AsynchronousChannelGroup.awaitTermination()
- java.util.concurrent.*各種阻塞方法,例如ExecutorService.awaitTermination() , Future.get() , BlockingQueue.take() , Semaphore.acquire() Condition.await()以及許多其他方法
- SwingUtilities.invokeAndWait()
請注意,阻塞I / O不會引發InterruptedException (這很可惜)。 如果所有這些類都聲明了InterruptedException ,那么您可能想知道何時會拋出此異常?
- 當某個線程在聲明InterruptedException某個方法上被阻塞并且您在該線程上調用Thread.interrupt()時,很可能阻塞的方法將立即拋出InterruptedException 。
- 如果您將任務提交到線程池( ExecutorService.submit() ),并且在執行任務時調用Future.cancel(true) 。 在這種情況下,線程池將嘗試為您中斷正在運行此類任務的線程,從而有效地中斷了您的任務。
知道InterruptedException實際含義后,我們就可以正確處理它。 如果有人試圖中斷我們的線程,而我們通過捕獲InterruptedException發現了它,則最合理的做法是讓該線程完成,例如:
class Cleaner implements Runnable, AutoCloseable {private final Thread cleanerThread;Cleaner() {cleanerThread = new Thread(this, "Cleaner");cleanerThread.start();}@Overridepublic void run() {try {while (true) {cleanUp();TimeUnit.SECONDS.sleep(1);}} catch (InterruptedException ignored) {log.debug("Interrupted, closing");}}//... @Overridepublic void close() {cleanerThread.interrupt();} }注意, try-catch塊現在圍繞while循環。 這樣,如果sleep()拋出InterruptedException ,我們將跳出循環。 您可能會爭辯說,我們應該記錄InterruptedException的堆棧跟蹤。 這取決于情況,因為在這種情況下中斷線程是我們真正希望的,而不是失敗。 但這取決于你。 最重要的是,如果sleep()被另一個線程中斷,我們將很快完全脫離run() 。 如果您非常小心,您可能會問,如果線程在cleanUp()方法中而不是在睡眠時中斷線程,會發生什么情況? 通常,您會遇到這樣的手動標記:
private volatile boolean stop = false;@Override public void run() {while (!stop) {cleanUp();TimeUnit.SECONDS.sleep(1);} }@Override public void close() {stop = true; }但是請注意, stop標志(必須是volatile !)不會中斷阻塞操作,我們必須等到sleep()完成。 另一方面,有人可能會爭辯說,顯式flag可以更好地控制我們,因為我們可以隨時監視其值。 事實證明,線程中斷的工作方式相同。 如果有人在執行非阻塞計算時中斷了線程(例如在cleanUp()內部),則此類計算不會立即中斷。 但是,線程被標記為已中斷,并且隨后的所有阻塞操作(例如sleep() )都將立即立即拋出InterruptedException因此我們不會丟失該信號。
如果我們編寫仍想利用線程中斷功能的非阻塞線程,那么我們也可以利用這一事實。 不必依賴于InterruptedException我們只需定期檢查Thread.isInterrupted() :
public void run() {while (Thread.currentThread().isInterrupted()) {someHeavyComputations();} }在上方,如果有人中斷了我們的線程,則在someHeavyComputations()返回時我們將立即放棄計算。 如果它運行了兩個長時間或無限期,我們將永遠不會發現中斷標志。 有趣的是, interrupted標志不是一次性的 。 我們可以調用Thread.interrupted()而不是isInterrupted() ,這將重置interrupted標志并且我們可以繼續。 有時您可能想忽略中斷標志并繼續運行。 在這種情況下, interrupted()可能會派上用場。 順便說一句,我(不精確地)稱“吸氣劑”來改變被觀察物體的狀態 “ 海森格 ”。
注意
如果您是老派程序員,則可能會想起Thread.stop()方法,該方法已被棄用10年了 。 在Java 8中,有計劃“取消實現” ,但是在1.8u5中,它仍然存在。 但是,不要使用它,而是使用Thread.stop()將任何代碼重構為Thread.interrupt() 。
番石榴的
很少您可能會完全忽略InterruptedException 。 在這種情況下,請查看Guava的Uninterruptibles 。 它具有許多實用方法,例如sleepUninterruptibly()或awaitUninterruptibly(CountDownLatch) 。 只是要小心他們。 我知道他們沒有聲明InterruptedException (可能很少),但是它們也完全防止了當前線程被中斷–這是非常不尋常的。
摘要
到現在為止,我希望您對為什么某些方法引發InterruptedException有所了解。 主要的收獲是:
- 捕獲的InterruptedException應該得到正確處理-大多數情況下,這意味著完全脫離當前任務/循環/線程
- 吞下InterruptedException很少是一個好主意
- 如果線程不在阻塞調用中時被中斷,請使用isInterrupted() 。 當線程已經被中斷時也進入阻塞方法應該立即拋出InterruptedException 。
翻譯自: https://www.javacodegeeks.com/2014/06/interruptedexception-and-interrupting-threads-explained.html
總結
以上是生活随笔為你收集整理的InterruptedException和中断线程的解释的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 交通标线设置(交通标线设置应遵循哪些原则
- 下一篇: 联想电脑指纹识别怎么设置(联想电脑指纹识