对于Java线程中断的理解,哪种情况下会响应中断?哪种情况下不响应中断?

您所在的位置:网站首页 线程阻塞状态 对于Java线程中断的理解,哪种情况下会响应中断?哪种情况下不响应中断?

对于Java线程中断的理解,哪种情况下会响应中断?哪种情况下不响应中断?

2023-07-17 04:10| 来源: 网络整理| 查看: 265

        线程状态有哪些?

        哪种情况下会响应中断?哪种情况下不响应中断?

        在响应中断的方法中,哪些方法会重置中断标志位?哪些方法不会重置中断标志位?

        本文会回答上面三个问题,这些也是学习中断的关键。最近在学习JUC框架的时候,发现了很多工具类都是支持可中断的,如AQS、FutureTask都是可以在线程执行中,支持对于中断的响应,所以需要对线程中断有个了解,才能更好的学习JUC的源码。

线程中断的作用:

           线程中断可以使一个线程从等待状态变成就绪状态,如果中断的线程正处于运行状态,那么这个中断是不会用任何作用的(表面上不会影响正在运行的线程),线程恢复到就绪状态后,可以继续执行逻辑代码,想要让一个线程从等待状态中恢复过来有三种发送:一.等待超时,二.得到一个通知,三.使用中断

           注意:使用线程中断,并不是要把线程给终止或是杀死,而是让线程不再继续等待,而是让线程不再继续等待,线程可以继续往下执行代码,线程发生中断后,会抛出一个中断的异常,决定如何处理就看业务代码怎么写了。

线程中断的原理:

         Thread.interrupt()方法仅仅是在当前线程中打了一个停止的标识将中断标志修改为true,并没有真正的停止线程。如果在此基础上进入堵塞状态(sleep(),wait(),join()),马上就会抛出一个InterruptedException,且中断标志被清除,重新设置为false。记住一点,当调用Thread.interrupt(),还没有进行中断时,此时的中断标志位是true,当发生中断之后(执行到sleep(),wait(),join()),这个时候的中断标志位就是false了。

         中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true; 对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false.但调用Thread.interrupted()会对中断状态进行复位。

Thread中关于中断的方法:

         public void interrupt()方法:中断线程,也就是改变中断标志位为true

          

       public static boolean interrupted()方法:静态方法,内部实现是调用的当前线程的isInterrupted(),并且会重置当前线程的中断状态

          

       public boolean isInterrupted()方法:判断线程是否被中断,返回ture代表线程被中断了,只是去获取一下,不会该表中断标志位。

线程状态图:

              

 

NEW 状态是指线程刚创建,尚未启动,不会出现在Dump中。

RUNNABLE 状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等,主要不同是runable里面有2个状态,可以理解为就是JVM调用系统线程的状态。

BLOCKED  受阻塞并等待监视器锁。这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 或者可重入的 synchronized块里别人调用wait() 方法, 也就是这里是线程在等待进入临界区

WAITING  无限期等待另一个线程执行特定操作。这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在临界点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束

TIMED_WAITING  有时限的等待另一个线程的特定操作。这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态

TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

响应中断的方法和不响应中断的方法:

     响应中断的方法: 线程进入等待或是超时等待的状态后,调用interrupt方法都是会响应中断的,所以响应中断的方法:Object.wait()、Thread.join、Thread.sleep、LockSupport.park的有参和无参方法。

     不响应中断的方法:线程进入阻塞状态后,是不响应中断的,等待进入synchronized的方法或是代码块,都是会被阻塞的,此时不会响应中断,另外还有一个不响应中断的,那就是阻塞在ReentrantLock.lock方法里面的线程,也是不响应中断的,如果想要响应中断,可以使用ReentrantLock.lockInterruptibly方法。

      其ReentrantLock底层是使用LockSupport.park方法进行等待的,前面说了LockSupport.park是响应中断的,当线程进入ReentrantLock.lock方法里面进行阻塞后,此时调用Thread.interrupt()方法之后,该线程是会被中断被唤醒的,但是唤醒之后,会调用LockSupport.park再次进入等待状态,所以仅从宏观(表面)上面看ReentrantLock.lock是不支持响应中断的,从微观(原理)上面讲ReentrantLock.lock内部确实中断了响应,但是还是会被迫进行等待状态。

清除中断标志位的方法:

        在明白了哪些方法会响应中断后,还需要明白响应中断方法的会不会清除中断标志位,对于Object.wait()、Thread.join、Thread.sleep方法都是会清除中断标志位了,这个上面已经说了,而对于LockSupport.park方法,是不会清除中断标志位的,不清除中断标志位意味着调用Thread.interrupt方法之后,碰到多个LockSupport.park方法线程不会进行等待状态了。

public class Test05 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println("线程一正在执行,将进入等待状态,当前时间= "+System.currentTimeMillis()+", 此时的中断标志位:"+Thread.currentThread().isInterrupted()); LockSupport.park(); System.out.println("线程一从等待状态中醒来,当前时间= "+System.currentTimeMillis()+", 此时的中断标志位:" + Thread.currentThread().isInterrupted()); LockSupport.park(); System.out.println("线程一从等待状态中醒来,当前时间= "+System.currentTimeMillis()+", 此时的中断标志位:"+Thread.currentThread().isInterrupted()); }); System.out.println("主线程正在执行"); thread.start(); System.out.println("主线程等待,睡眠两秒"); TimeUnit.SECONDS.sleep(2); thread.interrupt(); } } /* 执行结果: 主线程正在执行 主线程等待,睡眠两秒 线程一正在执行,将进入等待状态,当前时间 = 1592271902514, 此时的中断标志位:false 线程一从等待状态中醒来,当前时间 = 1592271904515, 此时的中断标志位:true 线程一从等待状态中醒来,当前时间 = 1592271904515, 此时的中断标志位:true */

         可以看到当线程一调用 Thread.interrupt方法之后,会从线程会从等待状态中醒来,然后第二次调用LockSupport.park时,根本就不会再次进入等待状态,所以调用LockSupport.park是不会清除中断标志位的。

         Object.wait()、Thread.join、Thread.sleep方法都是会清除中断标志位,这里就只演示Thread.sleep方法了,TimeUnit.SECONDS.sleep底层是调用Thread.sleep方法。

public class Test06 { public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { System.out.println("线程一正在执行,将进入等待状态,时间= "+System.currentTimeMillis()+",中断标志位:"+Thread.currentThread().isInterrupted()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { } System.out.println("线程一从等待状态中醒来,时间= "+System.currentTimeMillis()+",中断标志位:"+Thread.currentThread().isInterrupted()); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { } System.out.println("线程一从等待状态中醒来,时间= "+System.currentTimeMillis()+",中断标志位:"+Thread.currentThread().isInterrupted()); }); System.out.println("主线程正在执行"); thread.start(); System.out.println("主线程等待,睡眠两秒"); TimeUnit.SECONDS.sleep(2); thread.interrupt(); } } /* 执行结果: 主线程正在执行 主线程等待,睡眠两秒 线程一正在执行,将进入等待状态,时间= 1592272574294,中断标志位:false 线程一从等待状态中醒来,时间= 1592272576295,中断标志位:false 线程一从等待状态中醒来,时间= 1592272581295,中断标志位:false */

        可以看到中断标志位都是false,而且线程一第二次调用 Thread.sleep时,还是会进行等待状态的。

synchronized和ReentrantLock不响应中断的demo

     synchronized测试中断:

public class Test03 { //使用线程池进行测试 private static ExecutorService executorService = Executors.newFixedThreadPool(5); public static void main(String[] args) throws InterruptedException { Object luck = new Object(); executorService.execute(()->{ System.out.println("线程1打算获取锁"); synchronized (luck){ try { System.out.println("线程1打算睡眠"); //sleep不会释放锁 TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1已经执行完成"); } }); //主线程睡眠1秒,保证线程1已经睡眠了 TimeUnit.SECONDS.sleep(1); Thread thread = new Thread(() -> { System.out.println("线程2打算获取锁"); //线程2会在此被阻塞,因为线程1已经拿到了锁,并抱着锁睡觉了 //在此中断线程是不会有任何响应的 synchronized (luck) { try { //判断当前线程的中断为 System.out.println("线程2的中断标志位 " + Thread.currentThread().isInterrupted()); //虽然在synchronized (luck) {中不会响应,但是线程的中断标志位还是true,所以执行到此,会响应迟到的中断 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("InterruptedException 线程2的中断标志位 " + Thread.currentThread().isInterrupted()); } } System.out.println("线程2已经执行完成"); }); thread.start(); //此时中断线程2,测试线程2在等待获取锁的时候会不会相应中断 thread.interrupt(); } } /* 执行结果: 线程1打算获取锁 线程1打算睡眠 线程2打算获取锁 线程1已经执行完成 线程2的中断标志位 true java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at it.cast.basic.thread.interrupt.Test03.lambda$main$1(Test03.java:43) at java.lang.Thread.run(Thread.java:748) InterruptedException 线程2的中断标志位 false 线程2已经执行完成 */

        从上面例子可以得到,当线程等待进行synchronized的方法或是代码块,是不会响应中断的,如果此时调用Thread.interrupt方法是不会有任何方法,但是在阻塞的时候还是会讲中断标志位置位true的,那么在拿到锁之后,执行到TimeUnit.SECONDS.sleep(5);会迅速抛出一个中断的异常。

        ReentrantLock测试中断:

public class Test04 { private static ExecutorService executorService = Executors.newFixedThreadPool(5); public static void main(String[] args) throws InterruptedException { Lock lock = new ReentrantLock(); executorService.execute(()->{ System.out.println("线程1打算获取锁"); lock.lock(); try{ System.out.println("线程1打算睡眠"); //sleep不会释放锁 TimeUnit.SECONDS.sleep(5); }catch (InterruptedException e){ }finally { lock.unlock(); } System.out.println("线程1已经执行完成"); }); //主线程睡眠1秒,保证线程1已经睡眠了 TimeUnit.SECONDS.sleep(1); Thread thread = new Thread(() -> { System.out.println("线程2打算获取锁"); //在此中断线程是不会有任何响应的 lock.lock(); try { //判断当前线程的中断为 System.out.println("线程2的中断标志位 " + Thread.currentThread().isInterrupted()); //执行到此,会抛出中断异常,虽然这个中断操作是在lock.lock();进行的,但是会在 //执行sleep的时候响应,就是判断中断标志位 TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("InterruptedException 线程2的中断标志位 " + Thread.currentThread().isInterrupted()); } finally { lock.unlock(); } System.out.println("线程1已经执行完成"); }); thread.start(); thread.interrupt(); } } /* 执行结果: 线程1打算获取锁 线程1打算睡眠 线程2打算获取锁 java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at it.cast.basic.thread.interrupt.Test04.lambda$main$1(Test04.java:47) at java.lang.Thread.run(Thread.java:748) 线程1已经执行完成 线程2的中断标志位 true InterruptedException 线程2的中断标志位 false 线程1已经执行完成 */

           分析和synchronized一样,在进行线程2进行lock方法内部之后是不会响应中断的。

参考文档:

理解java线程的中断(interrupt)

深入理解java线程

【多线程】——深入理解线程中断方式(interrupt)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3