一、線程中斷
中斷可以理解為一個線程的標志位,與線程中斷有關的方法
- interrupt方法
Thread類的實例方法:中斷被調用線程,實際上只是改變了被調用線程 的內部中斷狀態,
並不會中斷線程
必須注意API中的這一段描述
If this thread is blocked in an invocation of the wait(), wait(long), or wait(long, int) methods of the Object class, or of the join(), join(long), join(long, int), sleep(long), or sleep(long, int), methods of this class, then its interrupt status will be cleared and it will receive an InterruptedException.
如果該線程被阻塞在的調用wait() , wait(long) ,或者wait(long, int)的方法的Object類,或者在join() join(long) , join(long, int) , sleep(long) ,或sleep(long, int)這個類的方法,那么它的中斷狀態將被清除,它還將收到一個InterruptedException 。
- interrupted方法
測試該線程對象是否被中斷,會清除中斷標志位
- isInterrupted方法
測試該線程對象是否被中斷,不會清除中斷標志位
二、實例測試
1. 打斷一個阻塞的線程,如sleep中的線程
@Slf4j(topic = "c.Test11")
public class Test11 {
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");
t1.interrupt();
Thread.sleep(1000);//防止interrupt方法未執行完畢,就繼續執行下一行代碼
log.debug("打斷標記:{}", t1.isInterrupted());
}
}
打斷了正在休眠中的線程,拋出了中斷異常,打斷狀態為false,被重置了
2、打斷一個正常運行的線程
Interrupt方法僅僅是對當前線程做了中斷‘標記’,但是否真的結束線程運行,並不是由Java來完成的,需要開發者自己判斷此標記,適當位置時機結束線程運行
測試:
@Slf4j(topic = "c.Test12")
public class Test12 {
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();
}
}
測試結果:
12:32:21.718 c.Test12 [main] - interrupt
12:32:21.720 c.Test12 [t1] - 被打斷了, 退出循環
三、如何正確使用Interrupt方法來中止線程
@Slf4j(topic = "c.TPTInterrupt")
public class TPTInterrupt {
private Thread thread;
public void start() {
thread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
if (current.isInterrupted()) {
log.debug("料理后事");
break;
}
try {
log.debug("即將進入休眠");
Thread.sleep(1000);
log.debug("將結果保存");
} catch (InterruptedException e) {
current.interrupt();
}
// 執行監控操作
}
}, "監控線程");
thread.start();
}
public void stop() {
log.debug("打斷線程");
thread.interrupt();
}
public static void main(String[] args) throws InterruptedException {
TPTInterrupt tptInterrupt = new TPTInterrupt();
tptInterrupt.start();
tptInterrupt.stop();
}
}
12:48:23.214 c.TPTInterrupt [main] - 打斷線程
12:48:23.214 c.TPTInterrupt [監控線程] - 即將進入休眠
12:48:23.217 c.TPTInterrupt [監控線程] - 料理后事
注意運行結果不是一定的。
小結一下:線程啟動后,我們執行了打斷,
如果不是在
Thread.sleep(1000);
即休眠中被打斷,那么中斷標志位將被正常設置為true。那繼續循環后將進入if中,料理后事然后退出
如果是在sleep中被打斷,那么會拋出InterruptedException
將會進入catch塊中,再次進行打斷(重新設置標志位)。
四、中斷park線程
測試用例:
@Slf4j(topic = "c.Test14")
public class Test14 {
private static void test4() {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
log.debug("park...");
LockSupport.park();
//如果打斷標記已經是 true, 則 park 會失效,所以這里使用interrupted方法清除了中斷狀態
log.debug("打斷狀態:{}", Thread.interrupted());
}
});
t1.start();
sleep(1);
t1.interrupt();
}
private static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark...");
// 執行了interrupt()后,中斷狀態設置為true
log.debug("打斷狀態:{}", Thread.currentThread().isInterrupted());
}, "t1");
t1.start();
sleep(1);
t1.interrupt();
}
public static void main(String[] args) throws InterruptedException {
test4();
}
}
LockSupport.park();
禁用線程,使其不被調度並處於休眠狀態。