java中使用interrupt通知线程停止详析

前言:

使用interrupt来通知线程停止运行,而不是强制停止!

 

普通情况停止线程

public class RightWayStopThreadWithoutSleep implements Runnable {

  @Override
  public void run() {
      int num = 0;
      while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2) {
          if (num % 10000 == 0) {
              System.out.println(num + "是1W的倍数");
          }
          num++;
      }
      System.out.println("任务运行结束!");
  }

  public static void main(String[] args) throws InterruptedException {
      Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
      thread.start();
      // 等待1s
      Thread.sleep(1000);
      // 通知停止线程
      thread.interrupt();
  }
}

使用thread.interrupt()通知线程停止

但是线程需要配合

while中使用Thread.currentThread().isInterrupted()检测线程当前的状态

运行结果:

……
……
221730000是1W的倍数
221740000是1W的倍数
221750000是1W的倍数
221760000是1W的倍数
221770000是1W的倍数
221780000是1W的倍数
221790000是1W的倍数
221800000是1W的倍数
任务运行结束!

Process finished with exit code 0

 

在可能被阻塞情况下停止线程

public class RightWayStopThreadWithSleep {
  public static void main(String[] args) throws InterruptedException {
      Runnable runnable = () -> {
          int num = 0;
          while (num <= 300 && !Thread.currentThread().isInterrupted()) {
              if (num % 100 == 0) {
                  System.out.println(num + "是100的倍数");
              }
              num++;
          }
          try {
              // 等个1秒,模拟阻塞
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              System.out.println("线程已停止!!");
              e.printStackTrace();
          }
      };

      Thread thread = new Thread(runnable);
      thread.start();
      // 等待时间要小于上面设置的1秒,不然线程都运行结束了,才执行到下面的thread.interrupt();代码
      Thread.sleep(500);
      // 通知停止线程
      thread.interrupt();
  }
}

线程在sleep 1秒的过程中,收到interrupt信号被打断,

线程正在sleep过程中响应中断的方式就是抛出InterruptedException异常

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
线程已停止!!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleep.lambda$main$0(RightWayStopThreadWithSleep.java:19)
at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

 

在每次迭代后都阻塞的情况下停止线程

public class RightWayStopThreadWithSleepEveryLoop {
  public static void main(String[] args) throws InterruptedException {
      Runnable runnable = () -> {
          int num = 0;
          try {
              while (num <= 10000) {
                  if (num % 100 == 0) {
                      System.out.println(num + "是100的倍数");
                  }
                  num++;
                  // 每次循环都要等待10毫秒,模拟阻塞
                  Thread.sleep(10);
              }
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      };
      Thread thread = new Thread(runnable);
      thread.start();
      // 5秒后通知停止线程
      Thread.sleep(5000);
      thread.interrupt();
  }
}

当每次迭代都会让线程阻塞一段时间的时候,在while/for循环条件判断时,

是不需要使用 *Thread.currentThread().isInterrupted()*判断线程是否中断的

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted

如果将上述代码中的 try/catch 放在 while 循环内:

public class RightWayStopThreadWithSleepEveryLoop {
  public static void main(String[] args) throws InterruptedException {
      Runnable runnable = () -> {
          int num = 0;
          while (num <= 10000) {
              if (num % 100 == 0) {
                  System.out.println(num + "是100的倍数");
              }
              num++;
              try {
                  // 每次循环都要等待10毫秒,模拟阻塞
                  Thread.sleep(10);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      };
      Thread thread = new Thread(runnable);
      thread.start();
      // 5秒后通知停止线程
      Thread.sleep(5000);
      thread.interrupt();
  }
}

运行结果:

0是100的倍数
100是100的倍数
200是100的倍数
300是100的倍数
400是100的倍数
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at stopthreads.RightWayStopThreadWithSleepEveryLoop.lambda$main$0(RightWayStopThreadWithSleepEveryLoop.java:18)
at java.lang.Thread.run(Thread.java:748)
500是100的倍数
600是100的倍数
700是100的倍数
……
……

会发现虽然抛出了异常,但是程序并没有停止,还在继续输出,

即使在while条件判断处添加!Thread.currentThread().isInterrupted()条件,依然不能停止程序!

原因是

java语言在设计sleep()函数时,有这样一个理念:

就是当它一旦响应中断,便会把interrupt标记位清除。

也就是说,虽然线程在sleep过程中收到了interrupt中断通知,并且也捕获到了异常、打印了异常信息,

但是由于sleep设计理念,导致Thread.currentThread().isInterrupted()标记位会被清除,

所以才会导致程序不能退出。

这里如果要停止线程,只需要在catch内 再调用一次interrupt();方法

try {
  // 每次循环都要等待10毫秒,模拟阻塞
  Thread.sleep(10);
} catch (InterruptedException e) {
  e.printStackTrace();
  Thread.currentThread().interrupt();
}

所以说,不要以为调用了interrupt()方法,线程就一定会停止。

 

两种停止线程最佳方法

1. 捕获了InterruptedException之后的优先选择:在方法签名中抛出异常

public class RightWayStopThreadInProd implements Runnable {

  public static void main(String[] args) throws InterruptedException {
      Thread thread = new Thread(new RightWayStopThreadInProd());
      thread.start();
      Thread.sleep(1000);
      thread.interrupt();
  }
  @Override
  public void run() {
      while (true) {
          System.out.println("go...");
          try {
              throwInMethod();
          } catch (InterruptedException e) {
              // 捕获异常,进行保存日志、停止程序等操作
              System.out.println("stop");
              e.printStackTrace();
          }
      }
  }
  /**
   * 如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch
   * 这样调用方才能捕获异常并作出其它操作
   * @throws InterruptedException
   */
  private void throwInMethod() throws InterruptedException {
      Thread.sleep(2000);
  }
}

如果方法内要抛出异常,最好是将异常抛出去,由顶层的调用方去处理,而不是try/catch

这样调用方才能捕获异常并做出其它操作。

2. 在catch中调用Thread.currentThread().interrupt();来恢复设置中断状态

public class RightWayStopThreadInProd2 implements Runnable {
  public static void main(String[] args) throws InterruptedException {
      Thread thread = new Thread(new RightWayStopThreadInProd2());
      thread.start();
      Thread.sleep(1000);
      thread.interrupt();
  }
  @Override
  public void run() {
      while (true) {
          if (Thread.currentThread().isInterrupted()) {
              System.out.println("程序运行结束");
              break;
          }
          reInterrupt();
      }
  }
  private void reInterrupt() {
      try {
          Thread.sleep(2000);
      } catch (InterruptedException e) {
          Thread.currentThread().interrupt();
          e.printStackTrace();
      }
  }
}

这里的if (Thread.currentThread().isInterrupted())判断,就是要你的代码有响应中断的能力。

 

总结

  • 调用interrupt方法不一定会中断线程
  • 通知线程停止,线程不会立即停止,而是会在合适的时候停止
  • 代码要有响应中断的能力

关于java中使用interrupt通知线程停止详析的文章就介绍至此,更多相关java interrupt 内容请搜索编程宝库以前的文章,希望以后支持编程宝库

 建造者模式概述建造者模式(Builder Pattern)属于创建型模式。它是将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。简而言之:建造者模式就是使用多个简单 ...