• 线程中断:Thread类中interrupt()、interrupted()和 isInterrupted()方法详解


    首先看看官方说明:

    interrupt()方法

    其作用是中断此线程(此线程不一定是当前线程,而是指调用该方法的Thread实例所代表的线程),但实际上只是给线程设置一个中断标志,线程仍会继续运行

    interrupted()方法

    作用是测试当前线程是否被中断(检查中断标志),返回一个boolean并清除中断状态,第二次再调用时中断状态已经被清除,将返回一个false。

    isInterrupted()方法

     

    作用是只测试此线程是否被中断 ,不清除中断状态

     下面我们进行测试说明:

     定义一个MyThread类,继承Thread,如下:

    public class MyThread extends Thread {
        @Override
        public  void run() {
            for (int i = 0; i < 10; i++) {
                System.out.println("i="+(i+1));
            }
        }
    }

    在main方法中测试:

    public class Do {
        public static void main(String[] args ) {
            MyThread thread=new MyThread();
            thread.start();
            thread.interrupt();
            System.out.println("第一次调用thread.isInterrupted():"+thread.isInterrupted());
            System.out.println("第二次调用thread.isInterrupted():"+thread.isInterrupted());
            System.out.println("thread是否存活:"+thread.isAlive());
        }
    }

    输出如下:

      从结果可以看出调用interrupt()方法后,线程仍在继续运行,并未停止,但已经给线程设置了中断标志,两个isInterrupted()方法都会输出true,也说明isInterrupted()方法并不会清除中断状态。

    下面我们把代码修改一下,多加两行调用interrupted()方法:

    public class Do {
        public static void main(String[] args ) {
            MyThread thread=new MyThread();
            thread.start();
            thread.interrupt();
            System.out.println("第一次调用thread.isInterrupted():"+thread.isInterrupted());
            System.out.println("第二次调用thread.isInterrupted():"+thread.isInterrupted());
                        //测试interrupted()函数
            System.out.println("第一次调用thread.interrupted():"+thread.interrupted());
            System.out.println("第二次调用thread.interrupted():"+thread.interrupted());
            System.out.println("thread是否存活:"+thread.isAlive());
        }
    }

    输出如下:

      从输出结果看,可能会有疑惑,为什么后面两个interrupted方法输出的都是false,而不是预料中的一个true一个false?注意!!!这是一个坑!!!上面说到,interrupted()方法测试的是当前线程是否被中断,当前线程!!!当前线程!!!,这里当前线程是main线程,而thread.interrupt()中断的是thread线程,这里的此线程就是thread线程。所以当前线程main从未被中断过,尽管interrupted()方法是以thread.interrupted()的形式被调用,但它检测的仍然是main线程而不是检测thread线程,所以thread.interrupted()在这里相当于main.interrupted()。对于这点,下面我们再修改进行测试。

    Thread.currentThread()函数可以获取当前线程,下面代码中获取的是main线程

    public class Do {
        public static void main(String[] args ) throws InterruptedException {
            Thread.currentThread().interrupt();
            System.out.println("第一次调用Thread.currentThread().interrupt():"
                    +Thread.currentThread().isInterrupted());
            System.out.println("第一次调用thread.interrupted():"
                    +Thread.currentThread().interrupted());
            System.out.println("第二次调用thread.interrupted():"
                    +Thread.currentThread().interrupted());
        }
    }

      这里都是针对当前线程在操作,如果interrupted()方法有检测中断并清除中断状态的作用,预料中的输出应该是true-true-false,实际输出如下:

     

    结果证明猜想是正确的。

      若果想要是实现调用interrupt()方法真正的终止线程,则可以在线程的run方法中做处理即可,比如直接跳出run()方法使线程结束,视具体情况而定,下面是一个例子。

    修改MyThread类:

    public class MyThread extends Thread {
        @Override
        public  void run() {
            for (int i = 0; i < 1000; i++) {
                System.out.println("i="+(i+1));
                if(this.isInterrupted()){
                    System.out.println("通过this.isInterrupted()检测到中断");
                    System.out.println("第一个interrupted()"+this.interrupted());
                    System.out.println("第二个interrupted()"+this.interrupted());
                    break;
                }
            }
            System.out.println("因为检测到中断,所以跳出循环,线程到这里结束,因为后面没有内容了");
        }
    }

    测试MyThread:

    public class Do {
        public static void main(String[] args ) throws InterruptedException {
            MyThread myThread=new MyThread();
            myThread.start();
            myThread.interrupt();
            //sleep等待一秒,等myThread运行完
            Thread.currentThread().sleep(1000);
            System.out.println("myThread线程是否存活:"+myThread.isAlive());
        }
    }

    结果:

    总结

      关于这三个方法,interrupt()是给线程设置中断标志;

              interrupted()是检测中断并清除中断状态;

              isInterrupted()只检测中断。

      还有重要的一点就是interrupted()作用于当前线程,interrupt()和isInterrupted()作用于此线程,即代码中调用此方法的实例所代表的线程。

     -------------------------------------------对线程中断的理解-------------------------------------------

    首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。
    所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
    而 Thread.interrupt 的作用其实也不是中断线程,而是「通知线程应该中断了」,
    具体到底中断还是继续运行,应该由被通知的线程自己处理。

    具体来说,当对一个线程,调用 interrupt() 时,
    ① 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。仅此而已。
    ② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,不受影响。

    interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
    也就是说,一个线程如果有被中断的需求,那么就可以这样做。
    ① 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。
    ② 在调用阻塞方法时正确处理InterruptedException异常。(例如,catch异常后就结束线程。)
    Thread thread = new Thread(() -> {
        while (!Thread.interrupted()) {
            // do more work.
        }
    });
    thread.start();
    
    // 一段时间以后
    thread.interrupt();
    具体到你的问题,Thread.interrupted()清除标志位是为了下次继续检测标志位。
    如果一个线程被设置中断标志后,选择结束线程那么自然不存在下次的问题,
    而如果一个线程被设置中断标识后,进行了一些处理后选择继续进行任务,
    而且这个任务也是需要被中断的,那么当然需要清除标志位了。

     例子:

    public class Test {
        public static void main(String[] args) throws InterruptedException {
            // sleepThread睡眠1000ms
            final Thread sleepThread = new Thread() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    super.run();
                }
            };
            // busyThread一直执行死循环
            Thread busyThread = new Thread() {
                @Override
                public void run() {
                    while (true);
                }
            };
            sleepThread.start();
            busyThread.start();
            sleepThread.interrupt(); // 改变中断状态标志位
            busyThread.interrupt();
            System.out.println(busyThread.isInterrupted());
            while (sleepThread.isInterrupted()); // 一直死循环等待该中断标志位被改变才继续执行下面的代码
            System.out.println("sleepThread isInterrupted: " + sleepThread.isInterrupted());
            System.out.println("busyThread isInterrupted: " + busyThread.isInterrupted());
        }
    
    }

    输出:

     

     出处:

      https://blog.csdn.net/qq_39682377/article/details/81449451

      https://www.zhihu.com/question/41048032

  • 相关阅读:
    Effective Java 第三版——72. 赞成使用标准异常
    Effective Java 第三版——71. 避免不必要地使用检查异常
    Effective Java 第三版——70. 对可恢复条件使用检查异常,对编程错误使用运行时异常
    Effective Java 第三版——69. 仅在发生异常的条件下使用异常
    Effective Java 第三版——68. 遵守普遍接受的命名约定
    Effective Java 第三版——67. 明智谨慎地进行优化
    Effective Java 第三版——66. 明智谨慎地使用本地方法
    Effective Java 第三版——65. 接口优于反射
    Effective Java 第三版——64. 通过对象的接口引用对象
    Effective Java 第三版——63. 注意字符串连接的性能
  • 原文地址:https://www.cnblogs.com/myseries/p/11107432.html
Copyright © 2020-2023  润新知