前言
在java Thread類中,我們會看到interrupt()、interrupted()及isInterrupted(),在大多數情況下,我們都不會使用到它們,但是有一個
InterruptedException
類我們應該是經常會遇到的,例如:
public static void main(String[] args) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
那麼你知道
InterruptedException
異常是如何觸發的嘛?
如何觸發InterruptedException
我們發現,在sleep()、wait()、join()等阻塞方法上才會需要抛
InterruptedException
:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
// 執行睡眠1秒,馬上會被主線程打斷
Thread.sleep(1000);
} catch (InterruptedException e) {
// 觸發InterruptedException異常
e.printStackTrace();
// 列印是否被打斷
System.out.println(Thread.currentThread().isInterrupted());
}
});
// 主線程啟動線程t1
t1.start();
// 主線程打斷t1
t1.interrupt();
// 等等t1執行完畢
t1.join();
}
1.我們建立了一個線程對象t1,t1執行邏輯就是sleep 1秒鐘;不過我們會發現線程t1根本sleep不了那麼久,因為主線程馬上就會打斷它;線程t1被打斷後,會列印出異常堆棧,并輸出線程t1的打斷标記;
2.在主線程中,啟動線程t1後,那麼就打斷線程t1;
3.等待線程t1邏輯全部執行完畢後主線程退出;
我們會發現,輸出的線程t1的打斷标記一緻是false;咱們明明已經調用了
t1.interrupt()
,并且也觸發了
InterruptedException
異常,這到底是為什麼導緻上面代碼線程t1的打斷标記一直是false呢?
我們從JDK源碼中找到了這樣一段注釋:

簡單翻譯如下:
如果任何線程打斷目前線程,目前線程的打斷标記在InterruptedException抛出時會被清除掉。
是以說,我們在捕捉到
InterruptedException
後想要再拿到線程t1的打斷标記基本上是不可能的。
interrupt()的作用
在Thread中,我們調用
interrupt()
并不會阻止目标線程繼續執行,它隻是給目标線程打上一個标記:
public static void main(String[] args) throws InterruptedException {
// 建立線程t1
Thread t1 = new Thread(() -> {
int i = 0;
// 循環自增
while (true) {
System.out.println(i);
i++;
// 判斷是否有打斷标記
if(Thread.currentThread().isInterrupted()){
System.out.println("線程被打斷,跳出循環");
// 如果有打斷标記,就跳出循環
break;
}
}
});
// 啟動線程t1
t1.start();
// 打斷線程t1
t1.interrupt();
// 等待線程t1執行完畢
t1.join();
}
1.在上述代碼中,如果删掉break代碼,那麼線程t1會一直死循環,說明interrupt()是無法阻止線程t1執行的;
2.在非阻塞代碼中,我們是可以拿到線程t1的打斷标記的,也就是說,非阻塞代碼不會清除線程的打斷标記;
interrupted()及isInterrupted()的差別
我們可以看一下Thread類中這兩個方法的源代碼:
// 靜态方法,調用目前線程的isInterrupted(true)
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 對象方法,調用目前線程對象的isInterrupted(false)
public boolean isInterrupted() {
return isInterrupted(false);
}
根據上面源碼,我們發現了
interrupted()
是一個靜态方法,是可以直接通過
Thread.interrupted()
調用的;
isInterrupted()
方法是線程對象方法,是需要通過線程對象調用的;我們在前面代碼中使用
Thread.currentThread().isInterrupted()
就是通過線程對象調用的;
另一個差別就是兩個方法傳遞的參數不同,
interrupted()
傳遞的true,
isInterrupted()
傳遞的是false;這兩個參數的作用是
是否清除打斷标記
,也就是說,如果調用
Thread.interrupted()
傳回true後,我們的打斷标記會被清除,那麼再次調用
Thread.interrupted()
拿到的就是false;
isInterrupted()
方法就不會清除打斷标記,每次調用
isInterrupted()
結果都不變;
小結
通過上述示例示範,我們可以總結出如下幾點: