当一个线程运行时,另外一个线程可以直接通过interrupt方法对其设置中断标志位。

判断线程是否中断的2个方法:

// 判断目标线程是否被中断,不会清除中断标记。
Thread.currentThread().isInterrupted()
// 判断目标线程是否被中断,会清除中断标记
Thread.interrupted()

示例1:判断目标线程是否被中断,不会清除中断标记

public class Task2 implements Runnable {
  @Override
  public void run() {
    System.out.println("线程的run方法开始执行");
    // 判断线程是否被中断,不会清除中断标记
    if (Thread.currentThread().isInterrupted()) {
      System.out.println("线程被中断");
    }
    if (!Thread.currentThread().isInterrupted()) {
      // 因为上面的Thread.currentThread().isInterrupted()不会清除中断标记
      // 因此线程保留了中断标记,所以该循环不会执行,程序不会输出1-3的数字
      for (int i = 0; i < 3; i++) {
        System.out.println("线程运行,i=" + i);
      }
    }
    System.out.println("线程的run方法执行结束");
  }
}


运行上面的测试类Task2InterruptTest,


程序运行结果:
线程的run方法开始执行
线程被中断
线程的run方法执行结束

以上的程序说明,Thread.currentThread().isInterrupted()不会清除中断标记,因此线程保留了中断标记,所以该循环不会执行,程序不会输出1-3的数字。

示例2:判断目标线程是否被中断,清除中断标记

public class Task3 implements Runnable {
  @Override
  public void run() {
    System.out.println("线程的run方法开始执行");
    // 判断线程是否被中断,会清除中断标记
    if (Thread.interrupted()) {
      System.out.println("线程被中断");
    }
    if (!Thread.currentThread().isInterrupted()) {
      // 因为上面的Thread.interrupted()会清除中断标记
      // 因此线程的中断标记没有了,线程继续执行,程序会输出1-3的数字
      for (int i = 0; i < 3; i++) {
        System.out.println("线程运行,i=" + i);
      }
    }
    System.out.println("线程的run方法执行结束");
  }
}


// 测试类
public class Task3InterruptTest {
  public static void main(String[] args) {
    Task3 task = new Task3();
    Thread thread = new Thread(task);
    thread.start();
    thread.interrupt(); // 中断thread线程的执行
  }
}


程序运行结果:
线程的run方法开始执行
线程被中断
线程运行,i=0
线程运行,i=1
线程运行,i=2
线程的run方法执行结束

以上的程序说明,Thread.interrupted()会清除中断标记,thread线程没有了中断标记,因此Thread.currentThread().isInterrupted()的结果为false,所以线程继续执行,程序会输出1-3的数字。

下面我们来看一下如果处于阻塞状态(调用了sleep、join等方法)的线程,被执行了中断操作,会有什么样的结果。

答案很明显,处于阻塞状态下的线程被执行了中断操作,会抛出中断异常InterruptedException。

示例3:处于阻塞状态的线程被执行中断操作的结果演示

public class Task4 implements Runnable {
  @Override
  public void run() {
    System.out.println("线程的run方法开始执行");
    try {
      Thread.sleep(5000); // 线程休眠5秒
      System.out.println("线程完成5秒钟的休眠");
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("线程的run方法执行结束");
  }
}


// 测试类
public class Task4InterruptTest {
  public static void main(String[] args) {
    Task4 task = new Task4();
    Thread thread = new Thread(task);
    thread.start();
    try {
      Thread.sleep(2000); // 主线程休眠2秒
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    thread.interrupt(); // 中断thread线程的执行
  }
}


程序运行结果:
线程的run方法开始执行
java.lang.InterruptedException: sleep interrupted
  at java.lang.Thread.sleep(Native Method)
  at testThread.day3.Task4.run(Task4.java:8)
  at java.lang.Thread.run(Unknown Source)
线程的run方法执行结束

通过上面的程序可以看出,处于阻塞状态下的线程被执行了中断操作,会抛出中断异常InterruptedException,但是thread线程在捕获到异常后,输出了“线程的run方法执行结束”的文字,说明thread线程在接收到中断指令后,并没有中断线程的执行,而是继续向下执行。

通过以上的程序示例,我们可以得出一个结论,那就是线程在执行了中断指令后,其实是给线程发了一个中断信号,线程被打上中断标记,如果线程没有对中断标记进行判断,做相应的处理,那么线程默认会继续执行,直到线程操作结束。

那么,问题来了,处于阻塞状态的线程被执行中断指令后,如何做到线程的中断呢,请看以下的代码。

示例4:处于阻塞状态的线程被执行中断指令后,立即把线程中断

public class Task5 implements Runnable {
  @Override
  public void run() {
    System.out.println("线程的run方法开始执行");
    try {
      Thread.sleep(5000); // 线程休眠5秒
      System.out.println("线程完成5秒钟的休眠");
    } catch (InterruptedException e) {
      // 在catch块里进行处理,再次调用interrupt方法,线程是否会中断执行呢?
      System.out.println("线程被中断");
      Thread.currentThread().interrupt();
    }
    for (int i = 0; i < 3; i++) {
      System.out.println(i);
    }
    System.out.println("线程的run方法执行结束");
  }
}


// 测试类
public class Task5InterruptTest {
  public static void main(String[] args) {
    Task5 task = new Task5();
    Thread thread = new Thread(task);
    thread.start();
    try {
      Thread.sleep(2000); // 主线程休眠2秒
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    thread.interrupt(); // 中断thread线程的执行
  }
}


程序运行结果
线程的run方法开始执行
线程被中断
0
1
2
线程的run方法执行结束

通过上面的程序,大家会感到奇怪,明明已经在catch块里,又调用了Thread.currentThread().interrupt()方法,但是线程还是没有中断,继续往下执行。

在这里再一次和大家要强调说明的就是,调用线程的interrupt()方法,不是说线程就不执行了,而是向线程发出了中断信号,线程被打上中断标记,如果想让线程中断,必须在run方法里对中断信号进行响应,让程序return返回到被调用处,才能使线程真正的中断。

示例5:正确的让线程中断的例子

public class Task6 implements Runnable {
  @Override
  public void run() {
    System.out.println("线程的run方法开始执行");
    try {
      Thread.sleep(5000); // 线程休眠5秒
      System.out.println("线程完成5秒钟的休眠");
    } catch (InterruptedException e) {
      // 在catch块里进行处理,让程序返回被调用处
      System.out.println("线程被中断");
      return;
    }
    for (int i = 0; i < 3; i++) {
      System.out.println(i);
    }
    System.out.println("线程的run方法执行结束");
  }
}


// 测试类
public class Task6InterruptTest {
  public static void main(String[] args) {
    Task6 task = new Task6();
    Thread thread = new Thread(task);
    thread.start();
    try {
      Thread.sleep(2000); // 主线程休眠2秒
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    thread.interrupt(); // 中断thread线程的执行
  }
}


程序运行结果:
线程的run方法开始执行
线程被中断

通过上面的程序,可以看出,使用return可以做到中断线程。

线程的中断其实是为了优雅的停止线程的运行,为了不使用stop方法而设置的。因为JDK不推荐使用stop方法进行线程的停止,因为stop方法会释放锁并强制终止线程,会造成执行一半的线程终止,带来数据的不一致性。