zoukankan      html  css  js  c++  java
  • JUC(2)线程中断机制

    如何停止、中断一个运行中的线程?

    • 首先一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend, Thread.resume 都已经被废弃了。
    • 其次,在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断。
    • 中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。
    • 中断协商机制并不是立刻马上停止一个线程。
    • Java 的每个线程对象里都有一个 boolean 类型的标识,代表是否有中断请求,可你寻遍 Thread 类你也不会找到这个标识,因为这是通过底层 native 方法实现的。
    • 若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设为true
    • 每个线程对象中都有一个标识,用于标识线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用

    如何使用中断标识停止线程

    • 在需要中断的线程中不断监听中断状态,一旦发生中断,就执行型对于的中断处理业务逻辑

    • 修改状态

    • 停止程序的运行

    • 三种中断标识停止线程的方式

      • 通过volatile

          	static volatile boolean isStop = false;
        		public static void m1() {
                new Thread(() -> {
                    while (true) {
                        if (isStop) {
                            System.out.println("-----isStop = true,程序结束。");
                            break;
                        }
                        System.out.println("------hello isStop");
                    }
                }, "t1").start();
        
                //暂停几秒钟线程
                try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
        
                new Thread(() -> {
                    isStop = true;
                }, "t2").start();
            }
        
      • 通过AtomicBoolean

          	static AtomicBoolean atomicBoolean = new AtomicBoolean(false);		
        		public static void m2() {
                new Thread(() -> {
                    while (true) {
                        if (atomicBoolean.get()) {
                            System.out.println("-----atomicBoolean.get() = true,程序结束。");
                            break;
                        }
                        System.out.println("------hello atomicBoolean");
                    }
                }, "t1").start();
        
                //暂停几秒钟线程
                try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
        
                new Thread(() -> {
                    atomicBoolean.set(true);
                }, "t2").start();
            }
        
      • 通过Thread类自带的中断的API方法实现

      public class InterruptDemo {
          public static void m3() {
            Thread t1 = new Thread(() -> {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("-----isInterrupted() = true,程序结束。");
                        break;
                    }
                    System.out.println("------hello Interrupt");
                }
            }, "t1");
            t1.start();
      
            //暂停几秒钟线程
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
      
            new Thread(() -> {
                t1.interrupt();//修改t1线程的中断标志位为true
            }, "t2").start();
        }
      }
      

    interrupt()

    interrupt() 方法是唯一一个可以将上面提到中断标志设置为 true 的方法,从这里可以看出,这是一个 Thread 类 public 的对象方法,所以可以推断出任何线程对象都可以调用该方法,进一步说明就是可以一个线程 interrupt 其他线程,也可以 interrupt 自己。其中,中断标识的设置是通过 native 方法 interrupt0 完成的

    如果这个线程因为被wait()、join()、sleep()方法阻塞时被打断(interupt),会清空中断标识并抛出InterruptedException(故而我们调用以上方法时强制我们try…catch…处理或抛出)其实被中断抛出 InterruptedException 的远远不止这几个方法,比如:

    反向推理,这些可能阻塞的方法如果声明有 throws InterruptedException , 也就暗示我们它们是可中断的

    由于他还会带来一个副作用就是清空中断标志位,中断标识也就重置为false,导致无限循环无法听下来,故而往往我们写代码时,会在catch时再调用一次interrupt方法确保线程被中断

    public class InterruptDemo {
        public static void main(String[] args) {
            Thread t1 = new Thread(() -> {
                while (true) {
                    if (Thread.currentThread().isInterrupted()) {
                        System.out.println("被中断,88");
                        break;
                    }
                    System.out.println("hello");
                    try {
                        TimeUnit.MILLISECONDS.sleep(500);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        e.printStackTrace();
                    }
                }
            }, "t1");
            t1.start();
          
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
          
            t1.interrupt();
        }
    }
    

    总的来说,当对一个线程,调用 interrupt() 时:

    • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。
    • 被设置中断标志的线程将继续正常运行,不受影响。所以, interrupt() 并不能真正的中断线程,需要被调用的线程自己进行配合才行。
    • 如果线程处于被阻塞状态(例如处于sleep, wait, join 等状态),在别的线程中调用当前线程对象的interrupt方法,那么线程将立即退出被阻塞状态,并抛出一个InterruptedException异常。

    isInterrupted()

    该方法返回中断标识的结果:

    • true:线程被中断
    • false:线程没被中断或被清空了中断标识

    拿到这个标识后,线程就可以判断这个标识来执行后续的逻辑了。

    interrupted()

    其实和上面的 isInterrupted() 方法差不多,两个方法都是调用 private 的 isInterrupted() 方法, 唯一差别就是会清空中断标识

    因为调用该方法,会返回当前中断标识,同时会清空中断标识:

    Thread.currentThread().isInterrupted(); // true
    Thread.interrupted() // true,返回true后清空了中断标识将其置为 false
    Thread.currentThread().isInterrupted(); // false
    Thread.interrupted() // false
    

    那么现实中,这个方法有什么用呢?

    当你可能要被大量中断并且你想确保只处理一次中断时,就可以使用这个方法了

    该方法在 JDK 源码中应用也非常多,比如:

    中断机制的使用场景

    通常,中断的使用场景有以下几个

    • 点击某个桌面应用中的关闭按钮时(比如你关闭 IDEA,不保存数据直接中断?)

    • 某个操作超过了一定的执行时间限制需要中止时;

    • 多个线程做相同的事情,只要一个线程成功其它线程都可以取消时;

    • 一组线程中的一个或多个出现错误导致整组都无法继续时;

      因为中断是一种协同机制,提供了更优雅中断方式,也提供了更多的灵活性,所以当遇到如上场景等,我们就可以考虑使用中断机制了

    使用中断机制有哪些注意事项

    其实使用中断机制无非就是注意上面说的两项内容:

    1. 中断标识
    2. InterruptedException

    前人已经将其总结为两个通用原则:

    原则-1

    如果遇到的是可中断的阻塞方法, 并抛出 InterruptedException,可以继续向方法调用栈的上层抛出该异常;如果检测到中断,则可清除中断状态并抛出 InterruptedException,使当前方法也成为一个可中断的方法

    原则-2

    若有时候不太方便在方法上抛出 InterruptedException,比如要实现的某个接口中的方法签名上没有 throws InterruptedException,这时就可以捕获可中断方法的 InterruptedException 并通过 Thread.currentThread.interrupt() 来重新设置中断状态。(前文interrupt()方法的最后一个例子)

  • 相关阅读:
    转 将python的datetime转换为unix时间戳
    VMware 虚拟机中添加新硬盘的方法
    UBUNTU 安装 nodejs
    ubuntu 20 查看site-package 目录
    基于C++代码的UE4学习(四)—— 定时器
    ObjectMapper 动态用法
    关于Mybatis中Mapper是使用XML还是注解的一些思考
    Spring Boot 中使用 Jedis 及 Lettuce的对比
    批量切换版本
    Build OpenJdk
  • 原文地址:https://www.cnblogs.com/zoran0104/p/15068018.html
Copyright © 2011-2022 走看看