1、线程中断
在Java中,线程中断是一种重要的线程间通信机制,用于通知线程应该停止当前正在执行的任务。以往,一些开发人员可能会使用 Thread.stop()、Thread.suspend() 和 Thread.resume() 这些方法来尝试停止线程,然而这些方法自Java 2平台标准版 (J2SE) 1.2 版本起就已经被标记为过时。因此,在开发中,我们应该以更安全的方式来使线程停止,例如通过改变一个标志位来让线程自主停止运行。
所以,我们推荐使用 interrupt() 方法来通知线程应该停止执行。通过调用 interrupt() 方法,可以设置线程的中断状态,然后在适当的时候检查这个中断状态并做出相应的处理,让线程安全地停止执行。这种做法既符合最佳实践,又能确保线程的安全性和可靠性。
2、相关API
interrupt方法()是通知指定线程中断,并且将该线程的中断位变为true,并不会将该线程直接中断。
isInterrupted方法()是返回指定线程的中断位标志。
interrupted方法()是返回指定线程的当前中断位标志,并且会将中断位变为flase。
3、如何使用中断标识停止线程
3.1正常运行状态下调用interrupt()
public class Demo {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println("进入线程A");
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("中断标志位被置为true,线程中断。。");
break;
}
System.out.println("线程A中断标志位为:"+Thread.currentThread().isInterrupted());
}
}, "a");
a.start();
new Thread(()->{
System.out.println("进入线程B");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.interrupt();
System.out.println("a.interrupt()调用之后,线程的中断标识值: "+a.isInterrupted()); //isInterrupted返回a线程的中断位标志
},"b").start();
}
}
上列代码中,线程A通过一个无限循环来执行任务。在每次循环中,它检查自身的中断状态(通过isInterrupted()),如果中断标志位被置为true,就输出相应的信息并跳出循环,以实现线程的停止。所以我们得出结论:
在A线程正常运行状态下,B线程调用 a.interrupt()方法会使得A线程的中断标志位由默认值false变为true,但此时A线程并不会中断,而是需要A线程去配合中断。
但如果我们在A线程阻塞状态(sleep、wait、join等)下调用interrupt()方法会发生什么?
3.2阻塞状态下调用interrupt()
public class Demo {
public static void main(String[] args) {
Thread a = new Thread(() -> {
System.out.println("进入线程A");
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("中断标志位被置为true,线程中断。。");
break;
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A中断标志位为:"+Thread.currentThread().isInterrupted());
}
}, "a");
a.start();
new Thread(()->{
System.out.println("进入线程B");
try {
Thread.sleep(3);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
a.interrupt();
System.out.println("线程B调用a.interrupt()已完成");
},"b").start();
}
}
上列代码我们在A线程的循环体中加入了睡眠3秒的操作,此时线程B调用 a.interrupt()。运行结果如下:
从上图我们发现两点:
1、打印出了java.lang.InterruptedException: sleep interrupted
2、线程B调用a.interrupt()后为什么线程A的中断标志位还是false?
我们先看第一个问题,这个问题interrupt()方法源码可以看见以下注释:
当线程在调用 Object 类的 wait()、wait(long)、wait(long, int) 方法,或者调用 Thread 类的 join()、join(long)、join(long, int)、sleep(long)、sleep(long, int) 方法时被阻塞,那么它的中断状态将会被清除,并且它会收到一个 InterruptedException。
所以我们得到第一个结论,线程如果在阻塞状态时调用**interrupt()**方法我们会收到一个 InterruptedException。
接下来我们看第二个问题,为什么线程B调用a.interrupt()为后什么线程A的中断标志位还是false?
我们从sleep方法中可以看见,抛出InterruptedException时,线程的中断标志位会被清除(变为false)。此时我们需要在catch方法中重新调用interrupt()方法将线程的中断标志位置为true。
Thread.currentThread().interrupt();
4、总结
- 在java中,我们想要可以使用interrupt()方法来将目标线程的中断标志位置为true,但此方法不会立即停止线程运行,需要目标线程配合。
- 在目标线程正常运行状态时,调用interrupt()可以正常将目标线程的中断标志位置为true。在目标线程阻塞状态时,调用interrupt()会出现异常并使得目标线程的中断标识位重新变为false。
- 我们也可以使用volatile变量或者原子类实现线程中断。