1.什么是中断机制
首先:
一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。
所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
其次:
在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。
因此,Java提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制。
它与 stop 最大的区别是:stop 是由系统强制终止线程,而线程中断则是给目标线程发送一个中断信号,如果目标线程没有接收线程中断的信号并结束线程,线程则不会终止,具体是否退出或者执行其他逻辑由目标线程决定。
实际案例:顾客在无烟餐厅中吸烟,服务员希望他别吸烟了,不是强行停止他吸烟,而是给他的标志位打为true,具体的停止吸烟还是要顾客自己停止。(体现了协商机制)
2.中断的相关方法
我们来看下线程中断最重要的 3 个方法,它们都是来自 Thread 类!
1、public void interrupt():仅仅是设置线程的中断状态为true,发起一个协商而不会立刻停止线程
2、public static boolean interrupted():判断线程是否被中断,并清除当前中断状态这个方法做了两件事:
(1)返回当前线程的中断状态,
(2)将当前线程的中断状态设为false
3、public boolean isInterrupted():通过检查中断标志位,判断当前线程是否被中断
3.使用中断标识停止线程
中断机制就是在需要中断的线程中 不断监听中断状态,一旦发生中断,就执行相应的中断处理业务逻辑stop线程,下面列出三种实现方法。
3.1.通过volatile变量实现
volatile保证了可见性,t2线程修改了标志位后能马上被t1线程看到
public class interruptDemo { // 设置中断标识 static volatile boolean isStop = false; public static void main(String[] args) throws InterruptedException { new Thread(()->{ // 通过while循环不断监听中断状态 while (true){ // 如果这个标志位被其他线程改为true了 if (isStop) { System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止"); break; } // 如果没停止,那就一直打印 System.out.println("t1 ------hello volatile"); } },"t1").start(); TimeUnit.MILLISECONDS.sleep(20); // 在短暂的暂停之后,设置中断标识为true new Thread(()->{ isStop = true; },"t2").start(); } }
执行结果:
t1 ------hello volatile t1 ------hello volatile t1 ------hello volatile t1 ------hello volatile t1 ------hello volatile t1 isStop被修改为true,程序终止
3.2.
public class interruptDemo { // 设置中断标识 static AtomicBoolean atomicBoolean = new AtomicBoolean(false); public static void main(String[] args) throws InterruptedException { new Thread(()->{ // 通过while循环不断监听中断状态 while(true){ // 如果这个标志位被其他线程改为true了 if(atomicBoolean.get()){ System.out.println(Thread.currentThread().getName()+"\t isStop被修改为true,程序终止"); break; } // 如果没停止,那就一直打印 System.out.println("t1 ------hello AtomicBoolean"); } },"t1").start(); TimeUnit.MILLISECONDS.sleep(20); new Thread(()->{ atomicBoolean.set(true); },"t2").start(); } }
执行结果:
t1 ------hello AtomicBoolean t1 ------hello AtomicBoolean t1 ------hello AtomicBoolean t1 ------hello AtomicBoolean t1 isStop被修改为true,程序终止
3.3.通过Thread类自带的中断api实现
默认的中断标志位是false,然后通过Thread类自带的interrupt方法被改为了true
public class interruptDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while (true) { // 通过while循环不断监听中断状态 if (Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为true,程序终止"); break; } // 如果没停止,那就一直打印 System.out.println("t1 ------hello interrupt "); } }, "t1"); t1.start(); TimeUnit.MILLISECONDS.sleep(20); new Thread(()->{ // 把t1中断 t1.interrupt(); },"t2").start(); } }
执行结果:
t1 ------hello interrupt t1 ------hello interrupt t1 ------hello interrupt t1 ------hello interrupt t1 ------hello interrupt t1 isInterrupted()被修改为true,程序终止
4.API源码分析
实例方法interrupt(),没有返回值:仅仅是设置一个中断标识位:
实例方法isInterrupted,返回布尔值:
public boolean isInterrupted() { return interrupted; }
private volatile boolean interrupted; //也调用了c底层
5.中断阻塞案例
在我们基本中断程序的骨架上 + 一个sleep阻塞,看看会出现什么情况。
public class interruptDemo { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while (true) { if(Thread.currentThread().isInterrupted()) { System.out.println(Thread.currentThread().getName()+"\t " + "中断标志位:"+Thread.currentThread().isInterrupted()+" 程序停止"); break; } try { Thread.sleep(200); } catch (InterruptedException e) { //加了这个,程序可以终止,只会爆异常 // Thread.currentThread().interrupt(); e.printStackTrace(); } System.out.println("-----hello InterruptDemo"); } }, "t1"); t1.start(); //暂停几秒钟线程 TimeUnit.SECONDS.sleep(1); new Thread(() -> t1.interrupt(),"t2").start(); } }
执行结果:
爆异常了,并且程序一直在跑
-----hello InterruptDemo -----hello InterruptDemo java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at com.fblinux.JUC.interruptDemo.lambda$main$0(interruptDemo.java:20) at java.base/java.lang.Thread.run(Thread.java:833) -----hello InterruptDemo -----hello InterruptDemo
加了Thread.currentThread().interrupt();
-----hello InterruptDemo -----hello InterruptDemo java.lang.InterruptedException: sleep interrupted at java.base/java.lang.Thread.sleep(Native Method) at com.fblinux.JUC.interruptDemo.lambda$main$0(interruptDemo.java:19) at java.base/java.lang.Thread.run(Thread.java:833) -----hello InterruptDemo t1 中断标志位:true 程序停止
原因说明:
1、中断标志位,默认false
2、线程t2 向线程t1发出了中断协商,t2调用t1.interrupt(),中断标志位true
3、中断标志位true,正常情况,程序停止,^_^
4、中断标志位true,异常情况,InterruptedException,将会把中断状态将被清除,并且将收到InterruptedException 。中断标志位false,导致无限循环
5、在catch块中,需要再次给中断标志位设置为true,2次调用停止程序才OK
6.总结
具体来说,当对一个线程调用interrupt()时:
1、如果线程处于正常活动状态,那么会将该线程的中断状态标志设置为true,仅此而已
被设置中断标志的线程将继续正常运行,不受影响。
所以,interrupt()并不能真正的中断线程,需要被调用的线程自己进行配合才行。
2、如果线程处于被阻塞状态(例如处于sleep、wait、join等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个interruptedException异常。
转载请注明:西门飞冰的博客 » JAVA 线程中断机制