聊起中斷,大家可能最熟悉的例子就是線程休眠。下麵就是一個線程休眠的 demo,在這個例子中,當我們調用 方法,該方法將會拋出一個需要捕獲的中斷異常,這裡捕獲該異常並直接返回。 java for (int i = 0; i An interrupt is an indication to a thre ...
聊起中斷,大家可能最熟悉的例子就是線程休眠。下麵就是一個線程休眠的 demo,在這個例子中,當我們調用 sleep
方法,該方法將會拋出一個需要捕獲的中斷異常,這裡捕獲該異常並直接返回。
for (int i = 0; i < somethings.size(); i++) {
// 休眠 4 s
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// 拋出中斷異常
return;
}
// 輸出
System.out.println(somethings.get(i));
}
除了 InterruptedException
中斷異常,另外還有三個中斷相關的方法,三個方法都與線程相關。
- thread#interrupt
- Thread#interrupted
- thread#isInterrupted
interrupt
方法用於中斷線程,但是並不是說該方法就能直接使線程停止。
下麵使用 interrupt
中斷線程,這裡我們期望中斷直接停止子線程輸出。但是當主線程調用子線程 interrupt
方法,子線程並卻沒有被終止,還在繼續列印數字。
Runnable interruptedTask=new Runnable() {
@Override
public void run() {
for (int i = 0; i <Integer.MAX_VALUE ; i++) {
System.out.println(i);
}
}
};
Thread interruptThread=new Thread(interruptedTask);
interruptThread.start();
// 休眠 5 s,
TimeUnit.SECONDS.sleep(2);
// 中斷當前線程
interruptThread.interrupt();
// 再次休眠,觀察子線程
TimeUnit.SECONDS.sleep(2);
引用 Java 官方對於中斷的解釋:
An interrupt is an indication to a thread that it should stop what it is doing and do something else. It's up to the programmer to decide exactly how a thread responds to an interrupt, but it is very common for the thread to terminate
中斷僅僅只是表明這個線程可以停止,但是線程是否停止完全取決於線程自己。只有線程相互協作,才能更好的停止線程。
每個線程都包含一個內部標誌,用來表示中斷狀態。調用線程的 interrupt
方法將會設置該狀態位,對於 Thread#sleep
等阻塞方法,將會拋出 InterruptedException
,並清除中斷標誌。
我們可以使用 thread#isInterrupted
或 Thread#interrupted
檢查中斷狀態。但是需要註意,兩個方法存在一些區別,Thread#interrupted
為靜態類方法,該方法檢測到中斷之後就會清除中斷標誌。
上面的方法我們只要加上中斷狀態判斷就也可以停止線程。
Runnable interruptedTask=new Runnable() {
@Override
public void run() {
for (int i = 0; i <Integer.MAX_VALUE ; i++) {
// 一旦檢測到中斷標誌,停止線程
if(Thread.interrupted()){
System.out.println("interrupted!!!!");
break;
}
System.out.println(i);
}
}
};
Thread interruptThread=new Thread(interruptedTask);
interruptThread.start();
// 休眠 5 s,
TimeUnit.SECONDS.sleep(2);
// 中斷當前線程
interruptThread.interrupt();
// 再次休眠,觀察子線程
TimeUnit.SECONDS.sleep(2);
}
中斷最佳實踐
不要隨意『吃掉』中斷異常
由於中斷異常是一個 checked exception,我們不得不需要處理該異常。如果我們可以保證該異常不影響應用,我們可以直接『吃掉』這個異常。其他情況下我們需要正確處理這個異常。
最簡單的做法就是不處理該異常,直接向上拋出中斷異常,讓上層調用者決定如何處理。
但是有些情況下,卻不適合上面的做法,這種情況下我們需要在 catch 中處理中斷。如果實在不知道如何處理,那就是記錄該異常,並使用日誌方式輸出。
中斷不會停止阻塞 IO
上面我們說到,對於一些阻塞方法如 Thread#sleep
,將會拋出中斷異常。但是對於 Socket 等阻塞 IO 調用,並不會拋出這個異常。也就是說中斷並不會停止阻塞 IO 的調用。
這是因為當調用 Thread#sleep
等阻塞方法時,Java 線程狀態將會從 RUNNABLE
轉變為 TIMED_WAITING
或 WATTING
。而當線程阻塞在 IO 讀取時,Java 線程實際狀態卻還是 RUNNABLE
。如果你對這個線程狀態還有疑惑,可以閱讀下這篇文章 面試官:都說阻塞 I/O 模型將會使線程休眠,為什麼 Java 線程狀態卻是 RUNNABLE?,深入理解一下線程狀態。
本文首發於studyidea.cn
歡迎關註我的公眾號:程式通事,獲得日常乾貨推送。如果您對我的專題內容感興趣,也可以關註我的博客:studyidea.cn