Java 中断线程(interrupt)超详细讲解

Java 中断线程(interrupt)

@author:Jingdai
@date:2020.10.06

概念

A 线程想让 B 线程终止运行,应该怎么办呢?在Java之前的版本中,可以利用 stop 方法来使一个线程终止,但是该方法已经被废弃了,不要这么用。

现在可以在 A 线程中调用 B 线程的 interrupt() 方法,来使 B 线程知道有线程想要使自己终止,但是是否终止取决于 B 线程自己,B 完全可以不理会这个终止请求。(当然最好不要这么做)下面我们来看看细节。

相关函数介绍

  • void interrupt()

中断这个线程。

  • boolean isInterrupted()

检查这个线程是否被中断。

  • static boolean interrupted()

    检查当前线程是否被中断,该方法在调用后还会清除该线程的中断状态。

示例解释

  • boolean isInterrupted() 方法和 static boolean interrupted() 方法区别

    如下代码首先测试 boolean isInterrupted() 方法。

    public static void main(String[] args) {
           
           
        Thread.currentThread().interrupt();
        System.out.println(Thread.currentThread().isInterrupted()); // true
        System.out.println(Thread.currentThread().isInterrupted()); // true
    }
    

    会发现两次输出都是 true

    接着测试 static boolean interrupted() 方法。

    public static void main(String[] args) {
           
           
        Thread.currentThread().interrupt();
        System.out.println(Thread.interrupted());  // true
        System.out.println(Thread.interrupted());  // false
    }
    

    测试发现第一次输出是 true ,而第二次输出是 false ,说明这个方法在调用后会清除线程的中断状态。

  • 中断且不理会

    这里我们让主线程中断线程 aThread ,但是 aThread 线程不理会,看会发生什么。代码如下:

    public static void main(String[] args){
           
           
        // create and start aThread
        Runnable r = () -> {
           
           
            while (true) {
           
           
                System.out.println("I'm alive");
            }
        };
        Thread aThread = new Thread(r);
        aThread.start();
    
        // interrupt aThread
        aThread.interrupt();
    }
    

    测试发现 aThread 线程的运行没有任何影响,还是一直输出 I'm alive

  • 一般用法

    既然如果中断不被处理的话不会有任何效果,按应该怎么响应中断呢?应该在线程中一直判断线程是否被中断,如果线程被中断,应该根据当前任务的要求选择接下来的工作,在结束线程前关闭必要的资源。看下面的代码。

    public static void main(String[] args){
           
           
        // create and start aThread
        Runnable r = () -> {
           
           
            try {
           
           
                while (!Thread.currentThread().isInterrupted()) {
           
           
                    //do work
                    System.out.println("I'm alive");
                }
                System.out.println("I ll die");
            } finally {
           
           
                // close some resources
            }
    
        };
        Thread aThread = new Thread(r);
        aThread.start();
    
        try {
           
           
            Thread.currentThread().sleep(1);
        } catch (InterruptedException e) {
           
           
            e.printStackTrace();
        }
        // interrupt aThread
        aThread.interrupt();
    }
    

    线程在没有被中断时,一直执行自己的任务,当被中断时,就会跳出循环,并在 finally 中关闭相应的资源和并做一些清理操作。

  • 线程被阻塞时

    上面的情况在线程没有被阻塞时可以运行,但是当线程被阻塞时,线程是无法检查线程的中断状态的,所以引入了 InterruptedException 异常帮助中断阻塞。不同的阻塞还有一些区别,具体如下。

    • 线程被 sleep() 方法或 join() 方法阻塞时

      如下代码:

      public static void main(String[] args) {
               
               
          // create and start aThread
          Runnable r = () -> {
               
               
              try {
               
               
                  Thread.currentThread().sleep(5000);
              } catch (InterruptedException e) {
               
               
                  System.out.println("die");
                  e.printStackTrace();
              } finally {
               
               
                  // close some resources
              }
          };
          Thread aThread = new Thread(r);
          aThread.start();
      
          try {
               
               
              Thread.currentThread().sleep(500);
          } catch (InterruptedException e) {
               
               
              e.printStackTrace();
          }
          // interrupt aThread
          aThread.interrupt();
      }
      

      在这些线程上调用 interrupt() 方法时,sleep()/join() 方法会被中断不再执行,它会清除中断状态并直接跳入 catch 块中执行,所以如果在循环中调用了 sleep() 方法,就不要去检测中断状态了,因为会直接跳入 catch 块中执行,应该直接捕获 InterruptedException 异常。

    • 线程被 wait() 方法阻塞时

      线程被 wait() 方法阻塞时,调用 interrupt() 方法的效果和线程被 sleep() 方法或 join() 方法阻塞的效果基本一样,它也会清除中断状态并跳入 catch 块中去执行。但是有一点点不同,就是被 interrupt() 调用的线程会先去获取调用 wait() 方法的对象的锁,获取完对象的锁之后才会抛出异常并进入 catch 块中去执行,如果获得不到锁,那么就无法抛出异常,也无法进入 catch 块中。如下代码,首先 aThread 获得锁并 wait(注意 wait() 方法会释放锁),然后 bThead 获得锁,但是 b 永远不释放锁,在 aThread wait 的时候,主线程调用 interrupt() 去中断 aThread,但是由于 aThread 永远无法获得锁,所以它不会抛出异常,也不会进入到 catch 块中运行。代码如下。

      public static void main(String[] args) {
               
               
              
          Runnable r = () -> {
               
               
              try {
               
               
                  synchronized (o) {
               
               
                      System.out.println("aThread get lock");
                      o.wait(10000);
                  }
              } catch (InterruptedException e) {
               
               
                  e.printStackTrace(); // can't come here
              } 
          };
          Thread aThread = new Thread(r);
          aThread.start();
      
          Runnable r2 = () -> {
               
               
              try {
               
               
                  Thread.currentThread().sleep(250);
                  synchronized (o) {
               
               
                      System.out.println("bThread get lock");
                      while (true) {
               
               
                      }
                  }
              } catch (InterruptedException e) {
               
               
                  e.printStackTrace();
              }
          };
          Thread bThread = new Thread(r2);
          bThread.start();
      
          try {
               
               
              Thread.currentThread().sleep(1000);
          } catch (InterruptedException e) {
               
               
              e.printStackTrace();
          }
          // interrupt aThread
          aThread.interrupt();
      }
      
    • 其他阻塞

      在 API 中还有其他的阻塞情况。如果线程被 InterruptibleChannel 的IO操作阻塞,则这个管道会被关闭,线程的中断状态会被置位,同时会抛出 ClosedByInterruptException 异常。

      如果线程被 Selector 阻塞,则这个线程的中断状态会被置位,并且立即从选择操作中返回,可能返回一个非零值,就像 selectorwakeup 方法被调用一样。

      这两种阻塞我都没有用过(太菜了),所以没有写代码去测试,之后遇到我会去测试。

      除了以上这几种情况,其他情况都会置位线程的中断状态。

参考

  • Java核心技术 卷1 12章
  • Java8 API

猜你喜欢

转载自blog.csdn.net/qq_41512783/article/details/108943193