中断线程的玩法 interrup()interrupted()isInterrupted()

Posted java叶新东老师

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中断线程的玩法 interrup()interrupted()isInterrupted()相关的知识,希望对你有一定的参考价值。

中断线程的使用场景

想象这样一个场景,当我们打开了一个杀毒软件,此时杀毒软件正在扫描你的磁盘文件是否有病毒,但是扫描到一半的时候,你发现电脑太卡了,想要关闭杀毒软件,这时候你点击右上角的 X 按钮,杀毒软件中所有正在扫描的线程就会中断扫描并且关闭线程的执行,当所有线程都退出后,杀毒软件也随之关闭;

中断在计算机中是一个很重要的机制,它决定你了计算机响应速度的快慢,假如没有中断机制,这时候你点击了关闭杀毒软件的按钮,杀毒软件却提示说必须要扫描完才能关闭窗口,你是不是会气的想摔电脑?

interrup()

java中的中断线程方法,需要注意的是,interrup()方法不会中断正在运行的线程,它的作用仅仅只是设置一个中断的标志位,就等于给这个线程设置了一个标签,并不会真正的中断;
在这里插入图片描述

interrupted()

此方法是用来测试中断状态,并且会清除线程的中断状态;返回值为boolean类型, true表示中断,false表示未中断; 所以第一次调用会返回true,同时清除中断标志,当第二次调用时会返回false,表示中断标志已被清除;
在这里插入图片描述

isInterrupted()

isInterrupted() 方法的作用是检查中断标志,判断线程是否被中断,不过isInterrupted只判断是否中断,不会清除中断状态,它的返回值是boolean类型,默认是 false,当我们调用interrup() 方法后,返回值就会变成true

如何中断运行中的线程

有人会说了,既然设置中断状态位不会使线程中断,那我要怎么中断正在运行的程序呢,其实很简单,一般情况下我们都是在循环里面做操作的,如果我们需要中断的时候,使用跳出循环关键字break或者return;返回出去就可以啦!
接下来,我们来一组代码测试下

package com.Lock;
/**
 * 线程中断操作
 *
 * interrupt 不会中断一个正在运行的线程
 * interrupt() 仅仅只是打一个停止标志,并不会真正地停止线程
 */
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i <100 ; i++) {
                // 判断是否中断
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("我已经停止了"+i);
                    System.out.println("清除标志:"+Thread.interrupted());
                    System.out.println("清除标志2:"+Thread.interrupted());
//                 退出线程
                    break;
                } else {
                    System.out.println("我正在运行中" + i);
                }
            }
        });
        thread.start();
        System.out.println("中断");
        // 设置中断标志
        thread.interrupt();
    }
}

打印结果可以看到,设置中断标志后,因为break的原因,线程立马停止了运行;并且在清除中断标志时,第一次返回true,第二次返回了false
在这里插入图片描述

interrupted()方法的坑

需要特别注意interrupted()这个方法,通过源码可以看到,里面是直接清除currentThread() 的中断标志,也就是说,你在哪个线程中调用interrupted() ,清除的就是哪个线程的中断标志;
在这里插入图片描述
接下来,我们用一组代码测试下,在下面的代码中,先中断T1线程, 然后在主线程中清除中断状态,但是这个清除操作是在main方法的线程里面操作的,就相当于调用了main.interrupted() ,所以T1线程的中断状态不会清除,因此下面的代码执行完后,T1线程依然还是中断状态

package com.Lock;
/**
 * 线程中断操作
 *
 * interrupt 不会中断一个正在运行的线程
 * interrupt() 仅仅只是打一个停止标志,并不会真正地停止线程
 */
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i <50 ; i++) {
                // 判断是否中断
                 if(Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"已经中断了"+i);
                } else {
                    System.out.println(Thread.currentThread().getName()+"正在运行中" + i);
                }
            }

        });
        thread.start();
        System.out.println("中断");
        // 设置中断标志
        thread.interrupt();

        System.out.println("恢复");
        // 恢复中断的线程(只能恢复当前中断的线程,在同一个线程下  thread.interrupted() 和Thread.interrupted() 没有区别,这里清除的是主线程的中断状态
        thread.interrupted();
    }
}

运行后,果然不出我所料,T1线程还是中断的

中断
T1正在运行中0
T1已经中断了1
T1已经中断了2
恢复
T1已经中断了3
T1已经中断了4
T1已经中断了5
T1已经中断了6
T1已经中断了7
T1已经中断了8
T1已经中断了9
T1已经中断了10
T1已经中断了11
T1已经中断了12
T1已经中断了13
T1已经中断了14
T1已经中断了15
T1已经中断了16
T1已经中断了17
T1已经中断了18
T1已经中断了19
T1已经中断了20
T1已经中断了21
T1已经中断了22
T1已经中断了23
T1已经中断了24
T1已经中断了25
T1已经中断了26
T1已经中断了27
T1已经中断了28
T1已经中断了29
T1已经中断了30
T1已经中断了31
T1已经中断了32
T1已经中断了33
T1已经中断了34
T1已经中断了35
T1已经中断了36
T1已经中断了37
T1已经中断了38
T1已经中断了39
T1已经中断了40
T1已经中断了41
T1已经中断了42
T1已经中断了43
T1已经中断了44
T1已经中断了45
T1已经中断了46
T1已经中断了47
T1已经中断了48
T1已经中断了49

Process finished with exit code 0

接下来我们改一下代码,在T1线程中恢复中断状态

package com.Lock;
/**
 * 线程中断操作
 *
 * interrupt 不会中断一个正在运行的线程
 * interrupt() 仅仅只是打一个停止标志,并不会真正地停止线程
 */
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i <50 ; i++) {
                // 判断是否中断
                if(Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName()+"已经中断了"+i);
                    System.out.println("恢复");
                    // 在T1线程清除中断标志
                    Thread.interrupted();
                } else {
                    System.out.println(Thread.currentThread().getName()+"正在运行中" + i);
                }
            }
        },"T1");
        thread.start();
        System.out.println("中断");
        // 设置中断标志
        thread.interrupt();
    }
}

通过结果可以看到,已经清除了T1线程的中断标志

中断
T1正在运行中0
T1已经中断了1
恢复
T1正在运行中2
T1正在运行中3
T1正在运行中4
T1正在运行中5
T1正在运行中6
T1正在运行中7
T1正在运行中8
T1正在运行中9
T1正在运行中10
T1正在运行中11
T1正在运行中12
T1正在运行中13
T1正在运行中14
T1正在运行中15
T1正在运行中16
T1正在运行中17
T1正在运行中18
T1正在运行中19
T1正在运行中20
T1正在运行中21
T1正在运行中22
T1正在运行中23
T1正在运行中24
T1正在运行中25
T1正在运行中26
T1正在运行中27
T1正在运行中28
T1正在运行中29
T1正在运行中30
T1正在运行中31
T1正在运行中32
T1正在运行中33
T1正在运行中34
T1正在运行中35
T1正在运行中36
T1正在运行中37
T1正在运行中38
T1正在运行中39
T1正在运行中40
T1正在运行中41
T1正在运行中42
T1正在运行中43
T1正在运行中44
T1正在运行中45
T1正在运行中46
T1正在运行中47
T1正在运行中48
T1正在运行中49

Process finished with exit code 0

当中断线程遇到阻塞方法 wait()join()sleep()

当线程处于阻塞状态,调用 wait()join()sleep()方法时;调用线程对象的interrupt()方法会清除中断状态,并得到一个InterruptedException异常。
在这里插入图片描述

还是上代码吧,直观些

package com.Lock;
/**
 * 线程中断操作
 * <p>
 * interrupt 不会中断一个正在运行的线程
 * interrupt() 仅仅只是打一个停止标志,并不会真正地停止线程
 */
public class InterruptTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 50; i++) {
                // 判断是否中断
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "已经中断了" + i);
                    try {
                        // 线程在阻塞状态下被中断时,首先会先清除中断标志,并得到一个InterruptedException 异常
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(Thread.currentThread().getName() + "正在运行中" + i);
                }
            }
        }, "T1");
        thread.start();

        System.out.println("中断");
        // 设置中断标志
        thread.interrupt();

    }
}

打印结果可以看到,虽然已经被中断,但是打印的结果仍然是运行中的日志,并抛出了异常,所以根据此次结果断定,在阻塞时设置中断状态会被恢复,并抛出异常!

中断
T1正在运行中0
T1已经中断了1
T1正在运行中2
T1正在运行中3
T1正在运行中4
T1正在运行中5
T1正在运行中6
T1正在运行中7
T1正在运行中8
T1正在运行中9
T1正在运行中10
T1正在运行中11
T1正在运行中12
T1正在运行中13
T1正在运行中14
T1正在运行中15
T1正在运行中16
T1正在运行中17
T1正在运行中18
T1正在运行中19
T1正在运行中20
T1正在运行中21
T1正在运行中22
T1正在运行中23
T1正在运行中24
T1正在运行中25
T1正在运行中26
T1正在运行中27
T1正在运行中28
T1正在运行中29
T1正在运行中30
T1正在运行中31
T1正在运行中32
T1正在运行中33
T1正在运行中34
T1正在运行中35
T1正在运行中36
T1正在运行中37
T1正在运行中38
T1正在运行中39
T1正在运行中40
T1正在运行中41
T1正在运行中42
T1正在运行中43
T1正在运行中44
T1正在运行中45
T1正在运行中46
T1正在运行中47
T1正在运行中48
T1正在运行中49
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.Lock.InterruptTest.lambda$main$0(InterruptTest.java:20)
	at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

java 的线程中断是不是很简单呢? 其实java作为面向对象的语言,大部分的功能用起来都是很简单的,只是原理稍微复杂些,但是只要肯钻研下去,等你学会之后也不会觉得很复杂!

以上是关于中断线程的玩法 interrup()interrupted()isInterrupted()的主要内容,如果未能解决你的问题,请参考以下文章

java多线程--中断线程

interrupt interrupted isInterrupted 区别

并发编程:取消与关闭

JVM指令重排

Java多线程系列---“基础篇”09之 interrupt()和线程终止方式

时钟中断TIMER_BH(bottom_half)实现分析