interrupt 方法打斷線程
- case1:打斷sleep, wait, join (阻塞)的線程
- case2:打斷運行中的線程
打斷阻塞狀態中的線程,會拋出一個InterruptedException
異常,並且會重置打斷標記
。
- 介紹一下打斷標記
Thread類裏有一個isInterrupted
方法可以返回當前線程是否被打斷,被打斷返回true
,否則false
。
public boolean isInterrupted() {
return isInterrupted(false); // 這裏的false指不會重置打斷標記
}
還有一個類似的靜態方法interrupted
也可以返回當前線程是否被打斷,被打斷返回true
,否則false
,但是會重置打斷標記爲false。
public static boolean interrupted() {
return currentThread().isInterrupted(true); // 這裏的true指會重置打斷標記
}
打斷阻塞狀態的線程
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("sleep...");
try {
Thread.sleep(5000); // wait, join
} catch (InterruptedException e) {
e.printStackTrace();
}
},"t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
log.debug("打斷標記:{}", t1.isInterrupted()); // 1
t1.interrupt();
log.debug("打斷標記:{}", t1.isInterrupted()); // 2
}
}
/*Output:
22:29:08.624 c.Test11 [t1] - sleep...
22:29:09.621 c.Test11 [main] - interrupt
22:29:09.621 c.Test11 [main] - 打斷標記:false
22:29:09.624 c.Test11 [main] - 打斷標記:false
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.itcast.test.Test11.lambda$main$0(Test11.java:12)
at java.lang.Thread.run(Thread.java:748)
*/
上面兩個false是意義不同的,第一個代表當前線程正常運行,沒有被打斷;而第二個false代表線程在sleep, wait, join 中(阻塞狀態)被打斷,會拋出異常並將打斷標記重置爲false。
打斷正常運行的線程
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true) {
System.out.println("isInterrupted狀態: " + (Thread.currentThread().isInterrupted()));
}
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
}
}
/*Output:
...
isInterrupted狀態: false
isInterrupted狀態: false
isInterrupted狀態: false
22:43:30.383 c.Test12 [main] - interrupt
isInterrupted狀態: true
isInterrupted狀態: true
isInterrupted狀態: true
...
打斷後標記變爲true, 但是進程並沒有停止,那麼這個打斷到底有啥用啊?我們怎麼讓被打斷後的進程停下來哪?我們可以藉助打斷標記,如果打斷標記爲真,就手動退出進程。
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true) {
boolean interrupted = Thread.currentThread().isInterrupted();
if(interrupted) {
log.debug("被打斷了, 退出循環");
break;
}
}
}, "t1");
t1.start();
Thread.sleep(1000);
log.debug("interrupt");
t1.interrupt();
}
}
/*Output:
22:52:17.742 c.Test12 [main] - interrupt
22:52:17.747 c.Test12 [t1] - 被打斷了, 退出循環
*/
interrupt應用:多線程設計模式之兩階段終止
Two Phase Termination
:在一個線程 T1 中如何“優雅”終止線程 T2?這裏的【優雅】指的是給 T2 一個料理後事的機會。
錯誤思路
- 使用線程對象的 stop() 方法停止線程
- stop 方法會真正殺死線程,如果這時線程鎖住了共享資源,那麼當它被殺死後就再也沒有機會釋放鎖, 其它線程將永遠無法獲取鎖
- 使用 System.exit(int) 方法停止線程
- 目的僅是停止一個線程,但這種做法會讓整個程序都停止
兩階段終止模式
- 應用場景:後臺監控線程(需要設置停止選項)
- 思路
@Slf4j(topic = "c.TwoPhaseTermination")
public class Test_1 {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination_1 tpt = new TwoPhaseTermination_1();
tpt.start();
Thread.sleep(3500);
log.debug("停止監控");
tpt.stop();
}
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination_1 {
// 監控線程
private Thread monitorThread;
// 啓動監控線程
public void start() {
monitorThread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
// 是否被打斷
if (current.isInterrupted()) {
log.debug("料理後事");
break;
}
try {
Thread.sleep(1000); // 睡眠時被打斷
log.debug("執行監控記錄"); // 正常打斷
} catch (InterruptedException e) {
e.printStackTrace();
// 當拋出異常時,重新設置打斷標記
current.interrupt();
}
}
}, "monitor");
monitorThread.start();
}
// 停止監控線程
public void stop() {
monitorThread.interrupt();
}
}
/*Output:
00:30:54.263 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:55.263 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:56.265 c.TwoPhaseTermination [monitor] - 執行監控記錄
00:30:56.765 c.TwoPhaseTermination [main] - 停止監控
00:30:56.765 c.TwoPhaseTermination [monitor] - 料理後事
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at cn.itcast.test.TwoPhaseTermination_1.lambda$start$0(Test_1.java:33)
at java.lang.Thread.run(Thread.java:748)
*/
在睡眠時被打斷,拋出異常並優雅的退出線程。正常運行時被打斷,優雅的退出線程。