Java處置InterruptedException異常的實際與理論。本站提示廣大學習愛好者:(Java處置InterruptedException異常的實際與理論)文章只能為提供參考,不一定能成為您想要的結果。以下是Java處置InterruptedException異常的實際與理論正文
媒介
關於 InterruptedException,一種罕見的處置方法是 “生吞(swallow)” 它 —— 捕獲它,然後甚麼也不做(或許記載下它,不外這也好不到哪去)—— 就像前面的 清單 4 一樣。不幸的是,這類辦法疏忽了如許一個現實:這時代能夠產生中止,而中止能夠招致運用法式損失實時撤消運動或封閉的才能。
壅塞辦法
當一個辦法拋出 InterruptedException 時,它不只告知您它可以拋出一個特定的檢討異常,並且還告知您其他一些工作。例如,它告知您它是一個壅塞(blocking)辦法,假如您呼應適合的話,它將測驗考試清除壅塞並盡早前往。
壅塞辦法分歧於普通的要運轉較長時光的辦法。普通辦法的完成只取決於它所要做的工作,和能否有足夠多可用的盤算資本(CPU 周期和內存)。而壅塞辦法的完成還取決於一些內部的事宜,例如計時器到期,I/O 完成,或許另外一個線程的舉措(釋放一個鎖,設置一個標記,或許將一個義務放在一個任務隊列中)。普通辦法在它們的任務做完後便可停止,而壅塞辦法較難於猜測,由於它們取決於內部事宜。壅塞辦法能夠影響呼應才能,由於難於猜測它們什麼時候會停止。
壅塞辦法能夠由於等不到所等的事宜而沒法終止,是以令壅塞辦法可撤消 就異常有效(假如長時光運轉的非壅塞辦法是可撤消的,那末平日也異常有效)。可撤消操作是指能從內部使之在正常完成之前終止的操作。由 Thread 供給並受 Thread.sleep() 和 Object.wait() 支撐的中止機制就是一種撤消機制;它許可一個線程要求另外一個線程停滯它正在做的工作。當一個辦法拋出 InterruptedException 時,它是在告知您,假如履行該辦法的線程被中止,它將測驗考試停滯它正在做的工作而提早前往,並經由過程拋出 InterruptedException 注解它提早前往。 行動優越的壅塞庫辦法應當能對中止作出呼應並拋出 InterruptedException,以便可以或許用於可撤消運動中,而不至於影響呼應。
線程中止
每一個線程都有一個與之相干聯的 Boolean 屬性,用於表現線程的中止狀況(interrupted status)。中止狀況初始時為 false;當另外一個線程經由過程挪用 Thread.interrupt() 中止一個線程時,會湧現以下兩種情形之一。假如誰人線程在履行一個初級可中止壅塞辦法,例如 Thread.sleep()、 Thread.join() 或 Object.wait(),那末它將撤消壅塞並拋出 InterruptedException。不然, interrupt() 只是設置線程的中止狀況。 在被中止線程中運轉的代碼今後可以輪詢中止狀況,看看它能否被要求停滯正在做的工作。中止狀況可以經由過程 Thread.isInterrupted() 來讀取,而且可以經由過程一個名為 Thread.interrupted() 的操作讀取和消除。
中止是一種協作機制。當一個線程中止另外一個線程時,被中止的線程紛歧定要立刻停滯正在做的工作。相反,中止是禮貌地要求另外一個線程在它情願而且便利的時刻停滯它正在做的工作。有些辦法,例如 Thread.sleep(),很賣力地看待如許的要求,但每一個辦法不是必定要對中止作出呼應。關於中止要求,不壅塞然則依然要花較長時光履行的辦法可以輪詢中止狀況,並在被中止的時刻提早前往。 您可以隨便疏忽中止要求,然則如許做的話會影響呼應。
中止的協作特征所帶來的一個利益是,它為平安地結構可撤消運動供給更年夜的靈巧性。我們很少願望一個運動立刻停滯;假如運動在正在停止更新的時刻被撤消,那末法式數據構造能夠處於紛歧致狀況。中止許可一個可撤消運動來清算正在停止的任務,恢復不變量,告訴其他運動它要被撤消,然後才終止。
處置 InterruptedException
假如拋出 InterruptedException 意味著一個辦法是壅塞辦法,那末挪用一個壅塞辦法則意味著您的辦法也是一個壅塞辦法,並且您應當有某種戰略來處置 InterruptedException。平日最輕易的戰略是本身拋出 InterruptedException,如清單 1 中 putTask() 和 getTask() 辦法中的代碼所示。 如許做可使辦法對中止作出呼應,而且只需將 InterruptedException 添加到 throws 子句。
清單 1. 不捕獲 InterruptedException,將它流傳給挪用者
public class TaskQueue { private static final int MAX_TASKS = 1000; private BlockingQueue<Task> queue = new LinkedBlockingQueue<Task>(MAX_TASKS); public void putTask(Task r) throws InterruptedException { queue.put(r); } public Task getTask() throws InterruptedException { return queue.take(); } }
有時刻須要在流傳異常之進步行一些清算任務。在這類情形下,可以捕獲 InterruptedException,履行清算,然後拋出異常。清單 2 演示了這類技巧,該代碼是用於婚配在線游戲辦事中的玩家的一種機制。 matchPlayers() 辦法期待兩個玩家到來,然後開端一個新游戲。假如在一個玩家已到來,然則另外一個玩家仍未到來之際該辦法被中止,那末它會將誰人玩家放回隊列中,然後從新拋出 InterruptedException,如許誰人玩家對游戲的要求就不至於喪失。
清單 2. 在從新拋出 InterruptedException 之前履行特定於義務的清算任務
public class PlayerMatcher { private PlayerSource players; public PlayerMatcher(PlayerSource players) { this.players = players; } public void matchPlayers() throws InterruptedException { try { Player playerOne, playerTwo; while (true) { playerOne = playerTwo = null; // Wait for two players to arrive and start a new game playerOne = players.waitForPlayer(); // could throw IE playerTwo = players.waitForPlayer(); // could throw IE startNewGame(playerOne, playerTwo); } } catch (InterruptedException e) { // If we got one player and were interrupted, put that player back if (playerOne != null) players.addFirst(playerOne); // Then propagate the exception throw e; } } }
不要生吞中止
有時刻拋出 InterruptedException 其實不適合,例如當由 Runnable 界說的義務挪用一個可中止的辦法時,就是如斯。在這類情形下,不克不及從新拋出 InterruptedException,然則您也不想甚麼都不做。當一個壅塞辦法檢測到中止並拋出 InterruptedException 時,它消除中止狀況。假如捕獲到 InterruptedException 然則不克不及從新拋出它,那末應當保存中止產生的證據,以便挪用棧中更高層的代碼能曉得中止,並對中止作出呼應。該義務可以經由過程挪用 interrupt() 以 “從新中止” 以後線程來完成,如清單 3 所示。至多,每當捕獲到 InterruptedException 而且不從新拋出它時,就在前往之前從新中止以後線程。
清單 3. 捕獲 InterruptedException 後恢復中止狀況
public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException e) { // Restore the interrupted status Thread.currentThread().interrupt(); } } }
處置 InterruptedException 時采用的最蹩腳的做法是生吞它 —— 捕獲它,然後既不從新拋出它,也不從新斷言線程的中止狀況。關於不知若何處置的異常,最尺度的處置辦法是捕獲它,然跋文錄下它,然則這類辦法依然無異於生吞中止,由於挪用棧中更高層的代碼照樣沒法取得關於該異常的信息。(僅僅記載 InterruptedException 也不是明智的做法,由於比及人來讀取日記的時刻,再來對它作出處置就為時已晚了。) 清單 4 展現了一種應用得很普遍的形式,這也是生吞中止的一種形式:
清單 4. 生吞中止 —— 不要這麼做
// Don't do this public class TaskRunner implements Runnable { private BlockingQueue<Task> queue; public TaskRunner(BlockingQueue<Task> queue) { this.queue = queue; } public void run() { try { while (true) { Task task = queue.take(10, TimeUnit.SECONDS); task.execute(); } } catch (InterruptedException swallowed) { /* DON'T DO THIS - RESTORE THE INTERRUPTED STATUS INSTEAD */ } } }
假如不克不及從新拋出 InterruptedException,不論您能否籌劃處置中止要求,依然須要從新中止以後線程,由於一個中止要求能夠有多個 “吸收者”。尺度線程池 (ThreadPoolExecutor)worker 線程完成擔任中止,是以中止一個運轉在線程池中的義務可以起到兩重後果,一是撤消義務,二是告訴履行線程線程池正要封閉。假如義務生吞中止要求,則 worker 線程將不曉得有一個被要求的中止,從而延誤運用法式或辦事的封閉。
完成可撤消義務
說話標准中並沒無為中止供給特定的語義,然則在較年夜的法式中,難於保護除撤消外的任何中止語義。取決因而甚麼運動,用戶可以經由過程一個 GUI 或經由過程收集機制,例如 JMX 或 Web 辦事來要求撤消。法式邏輯也能夠要求撤消。例如,一個 Web 匍匐器(crawler)假如檢測到磁盤已滿,它會主動封閉本身,不然一個並行算法會啟動多個線程來搜刮處理計劃空間的分歧區域,一旦個中一個線程找到一個處理計劃,就撤消那些線程。
僅僅由於一個義務是可撤消的,其實不意味著須要立刻 對中止要求作出呼應。關於履行一個輪回中的代碼的義務,平日只需為每個輪回迭代檢討一次中止。取決於輪回履行的時光有多長,任何代碼能夠要花一些時光能力留意到線程曾經被中止(或許是經由過程挪用 Thread.isInterrupted() 辦法輪詢中止狀況,或許是挪用一個壅塞辦法)。 假如義務須要進步呼應才能,那末它可以更頻仍地輪詢中止狀況。壅塞辦法平日在進口就立刻輪詢中止狀況,而且,假如它被設置來改良呼應才能,那末還會拋出 InterruptedException。
唯一可以生吞中止的時刻是您曉得線程正要加入。只要當挪用可中止辦法的類是 Thread 的一部門,而不是 Runnable 或通用庫代碼的情形下,才會產生如許的場景,清單 5 演示了這類情形。清單 5 創立一個線程,該線程羅列素數,直到被中止,這裡還許可該線程在被中止時加入。用於搜刮素數的輪回在兩個處所檢討能否有中止:一處是在 while 輪回的頭部輪詢 isInterrupted() 辦法,另外一處是挪用壅塞辦法 BlockingQueue.put()。
清單 5. 假如曉得線程正要加入的話,則可以生吞中止
public class PrimeProducer extends Thread { private final BlockingQueue<BigInteger> queue; PrimeProducer(BlockingQueue<BigInteger> queue) { this.queue = queue; } public void run() { try { BigInteger p = BigInteger.ONE; while (!Thread.currentThread().isInterrupted()) queue.put(p = p.nextProbablePrime()); } catch (InterruptedException consumed) { /* Allow thread to exit */ } } public void cancel() { interrupt(); } }
弗成中止的壅塞辦法
並不是一切的壅塞辦法都拋出 InterruptedException。輸出和輸入流類會壅塞期待 I/O 完成,然則它們不拋出 InterruptedException,並且在被中止的情形下也不會提早前往。但是,關於套接字 I/O,假如一個線程封閉套接字,則誰人套接字上的壅塞 I/O 操作將提早停止,並拋出一個 SocketException。java.nio 中的非壅塞 I/O 類也不支撐可中止 I/O,然則異樣可以經由過程封閉通道或許要求 Selector 上的叫醒來撤消壅塞操作。相似地,測驗考試獲得一個外部鎖的操作(進入一個 synchronized 塊)是不克不及被中止的,然則 ReentrantLock 支撐可中止的獲得形式。
弗成撤消的義務
有些義務謝絕被中止,這使得它們是弗成撤消的。然則,即便是弗成撤消的義務也應當測驗考試保存中止狀況,以防在弗成撤消的義務停止以後,挪用棧上更高層的代碼須要對中止停止處置。清單 6 展現了一個辦法,該辦法期待一個壅塞隊列,直到隊列中湧現一個可用項目,而不論它能否被中止。為了便利別人,它在停止後在一個 finally 塊中恢復中止狀況,以避免褫奪中止要求的挪用者的權力。(它不克不及在更早的時刻恢復中止狀況,由於那將招致無窮輪回 —— BlockingQueue.take() 將在進口處立刻輪詢中止狀況,而且,假如發明中止狀況集,就會拋出 InterruptedException。)
清單 6. 在前往前恢復中止狀況的弗成撤消義務
public Task getNextTask(BlockingQueue<Task> queue) { boolean interrupted = false; try { while (true) { try { return queue.take(); } catch (InterruptedException e) { interrupted = true; // fall through and retry } } } finally { if (interrupted) Thread.currentThread().interrupt(); } }
總結
您可以用 Java 平台供給的協作中止機制來結構靈巧的撤消戰略。各運動可以自行決議它們是可撤消的照樣弗成撤消的,和若何對中止作出呼應,假如立刻前往會傷害運用法式完全性的話,它們還可以推延中止。即便您想在代碼中完整疏忽中止,也應當確保在捕獲到 InterruptedException 然則沒有從新拋出它的情形下,恢復中止狀況,以避免挪用它的代碼沒法獲知中止的產生。以上就是Java處置InterruptedException異常的實際與理論的全體內容,願望本文對年夜家有所贊助,假如有疑問迎接年夜家留言停止評論辯論。