如何在Java中處理InterruptedException
- java
1.簡介
在本教程中,我們將探討Java的InterruptedException
。首先,我們將通過插圖快速介紹線程的生命週期。接下來,我們將看到在多線程應用程序中進行工作可能如何導致InterruptedException
。最後,我們將看到如何處理此異常。
2.多線程基礎
在討論中斷之前,讓我們回顧一下多線程。多線程是同時執行兩個或多個線程的過程。 main()
方法相關聯的單個線程(稱為主線程)開始。然後,該主線程可以啟動其他線程。
線程是輕量級的,這意味著它們在相同的內存空間中運行。因此,他們可以輕鬆地相互交流。讓我們看一下線程的生命週期:
一旦我們創建了一個新線程,它就處於NEW
狀態。它將保持這種狀態,直到程序使用start()
方法啟動線程為止。
在線程上調用start()
方法會將其置於RUNNABLE
狀態。處於此狀態的線程正在運行或準備運行。
當線程正在等待監視器鎖定並嘗試訪問被某個其他線程鎖定的代碼時,它將進入BLOCKED
狀態。
可以通過各種事件(例如,調用wait()
方法)將線程置於WAITING狀態。在這種狀態下,一個線程正在等待來自另一個線程的信號。
當線程完成執行或異常終止時,它將以TERMINATED
狀態結束。線程可以被中斷,並且當線程被中斷時,它將拋出InterruptedException
。
在下一節中,我們將InterruptedException
並學習如何對其進行響應。
3.什麼是InterruptedException
?
當線程在等待,休眠或以其他方式佔用時被中斷時,將引發InterruptedException
換句話說,某些代碼在我們的線程上interrupt()
這是一個已檢查的異常,Java中的許多阻止操作都可以拋出該異常。
3.1 中斷
中斷系統的目的是提供一個定義明確的框架,以允許線程中斷其他線程中的任務(可能是耗時的任務)。考慮中斷的一種好方法是它實際上並不中斷正在運行的線程,它只是請求線程在下一個方便的時機中斷自己。
3.2 阻塞和可中斷方法
線程可能由於以下幾個原因而阻塞:等待從Thread.sleep
(),
喚醒,等待獲取鎖,等待I / O完成或等待另一個線程中的計算結果, 其中。
InterruptedException
,以便可以對其進行處理並執行糾正措施。 Java中有幾種方法會拋出InterruptedException
。這些包括Thread.sleep()
, Thread.join()
, Object
類wait()
()方法以及BlockingQueue
put()
和take()
方法,僅舉幾例。
3.3 線程中的中斷方法
讓我們快速看一下Thread
類中用於處理中斷的一些關鍵方法:
public void interrupt() { ... }
public boolean isInterrupted() { ... }
public static boolean interrupted() { ... }
Thread
提供了interrupt()
方法,並查詢線程是否已被中斷,我們可以使用isInterrupted()
方法。有時,我們可能希望測試當前線程是否已被中斷,如果已中斷,則立即拋出此異常。在這裡,我們可以使用interrupted()
方法:
if (Thread.interrupted()) {
throw new InterruptedException();
}
3.4 中斷狀態標誌
中斷機制使用稱為中斷狀態的標誌來實現。每個線程都有一個代表其中斷狀態boolean
調用Thread.interrupt()
設置此標誌。當線程通過調用static
方法Thread.interrupted()
檢查中斷時,將清除中斷狀態。
要響應中斷請求,我們必須處理InterruptedException.
我們將在下一部分中看到如何做到這一點。
4.如何處理InterruptedException
重要的是要注意線程調度是依賴於JVM的。這是自然的,因為JVM是虛擬機,並且需要本機操作系統資源來支持多線程。因此,我們不能保證線程永遠不會中斷。
中斷表明線程應該停止正在執行的操作並執行其他操作。更具體地講,如果我們編寫將通過執行一些代碼Executor
或者其他某個線程管理機制,我們需要確保我們的代碼響應及時中斷。否則,我們的應用程序可能會導致死鎖。
InterruptedException
實用策略很少。讓我們看看它們。
4.1 傳播InterruptedException
我們可以允許InterruptedException
向上傳播到調用堆棧,例如,通過throws
子句並讓調用者確定如何處理中斷。這可能涉及我們不捕獲異常或捕獲並重新拋出異常。讓我們嘗試通過一個示例來實現:
public static void propagateException() throws InterruptedException {
Thread.sleep(1000);
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
throw new InterruptedException();
}
}
在這裡,我們正在檢查線程是否被中斷,如果是,則拋出InterruptedException 。現在,讓我們調用propagateException()
方法:
public static void main(String... args) throws InterruptedException {
propagateException();
}
當我們嘗試運行這段代碼時,我們將收到一個帶有堆棧跟踪InterruptedException
Exception in thread "main" java.lang.InterruptedException
at org.1ju.concurrent.interrupt.InterruptExample.propagateException(InterruptExample.java:16)
at org.1ju.concurrent.interrupt.InterruptExample.main(InterruptExample.java:7)
儘管這是響應異常的最明智的方法,但有時我們無法拋出異常-例如,當我們的代碼是Runnable
的一部分時。在這種情況下,我們必須抓住它並恢復其狀態。我們將在下一節中介紹如何處理這種情況。
4.2 恢復中斷
在某些情況下,我們無法傳播InterruptedException.
例如,假設我們的任務是由Runnable
定義的,或者重寫了一個不會拋出任何已檢查異常的方法。在這種情況下,我們可以保留中斷。執行此操作的標準方法是恢復中斷狀態。
我們可以interrupt()
方法(它將標誌設置回true
),以便調用堆棧上方的代碼可以看到已發出中斷。例如,讓我們中斷一個線程並嘗試訪問其中斷狀態:
public class InterruptExample extends Thread {
public static Boolean restoreTheState() {
InterruptExample thread1 = new InterruptExample();
thread1.start();
thread1.interrupt();
return thread1.isInterrupted();
}
}
這是處理該中斷並恢復中斷狀態run()
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); //set the flag back to true } }
最後,讓我們測試一下狀態:
assertTrue(InterruptExample.restoreTheState());
儘管Java異常涵蓋了所有特殊情況和條件,但是我們可能希望拋出特定於代碼和業務邏輯的特定自定義異常。在這裡,我們可以創建我們的自定義異常來處理中斷。我們將在下一部分中看到它。
4.3 自定義異常處理
定制異常提供了添加標準Java異常不包含的屬性和方法的靈活性。因此,根據情況以自定義方式處理中斷是完全有效的。
我們可以完成其他工作,以允許應用程序正常處理中斷請求。例如,當一個線程正在休眠或等待I / O操作,並且接收到中斷時,我們可以在終止線程之前關閉任何資源。
讓我們創建一個稱為CustomInterruptedException
的自定義檢查異常:
public class CustomInterruptedException extends Exception {
CustomInterruptedException(String message) {
super(message);
}
}
當線程被中斷時,我們可以拋出CustomInterruptedException
:
public static void throwCustomException() throws Exception {
Thread.sleep(1000);
Thread.currentThread().interrupt();
if (Thread.interrupted()) {
throw new CustomInterruptedException("This thread was interrupted");
}
}
我們還看看如何檢查異常是否與正確的消息一起拋出:
@Test
public void whenThrowCustomException_thenContainsExpectedMessage() {
Exception exception = assertThrows(CustomInterruptedException.class, () -> InterruptExample.throwCustomException());
String expectedMessage = "This thread was interrupted";
String actualMessage = exception.getMessage();
assertTrue(actualMessage.contains(expectedMessage));
}
同樣,我們可以處理異常並恢復中斷狀態:
public static Boolean handleWithCustomException() throws CustomInterruptedException{
try {
Thread.sleep(1000);
Thread.currentThread().interrupt();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CustomInterruptedException("This thread was interrupted...");
}
return Thread.currentThread().isInterrupted();
}
我們可以通過檢查中斷狀態以確保其返回true
來測試代碼:
assertTrue(InterruptExample.handleWithCustomException());
5.結論
在本教程中,我們看到了處理InterruptedException
不同方法。如果我們正確處理它,我們可以平衡應用程序的響應能力和健壯性。