线程中断总结

在Java中,停止一个线程的主要机制是中断,中断并不是强迫终止一个线程,它是一种协作机制,是给线程传递一个取消信号,但是由线程来决定如何以及何时退出。

Thread类定义了如下方法:

public boolean isInterrupted();//测试此线程是否已被中断。此方法不影响线程的中断状态
public void interrupt();//中断线程
public static boolean interrupted();//测试此线程是否已被中断,并清空中断标志位

interrupt()对线程的影响与线程的状态和在进行的IO操作有关,我们先考虑线程的状态:

  • RUNNABLE:线程在运行或具备运行条件只是在等待操作系统调度
  • WAITING/TIMED_WAITING:线程在等待某个条件或超时
  • BLOCKED:线程在等待锁,试图进入同步块
  • NEW / TERMINATED:线程还未启动或已结束

RUNNABLE

如果线程在运行中,且没有执行IO操作,interrupt()只是会设置线程的中断标志位,没有任何其它作用。线程应该在运行过程中合适的位置检查中断标志位,如:

public class InterruptRunnableDemo extends Thread {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            // ... 单次循环代码
        }
        System.out.println("done ");
    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new InterruptRunnableDemo();
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }
}

 

WAITING/TIMED_WAITING

线程执行如下方法会进入WAITING状态:

public final void join() throws InterruptedException
public final void wait() throws InterruptedException

执行如下方法会进入TIMED_WAITING状态:

public final native void wait(long timeout) throws InterruptedException;
public static native void sleep(long millis) throws InterruptedException;
public final synchronized void join(long millis) throws InterruptedException

在这些状态时,对线程对象调用interrupt()会使得该线程抛出InterruptedException,需要注意的是,抛出异常后,中断标志位会被清空,而不是被设置。比如:

  public static void main(String[] args) throws InterruptedException {
    Thread t = new Thread() {
      @Override
      public void run() {
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          System.out.println(isInterrupted());
        }
      }
    };
    t.start();
    try {
      Thread.sleep(100);
    } catch (InterruptedException e) {
    }
    t.interrupt();
  }

输出为:

false

捕获到InterruptedException,通常表示希望结束该线程,线程大概有两种处理方式:

    • 向上传递该异常,这使得该方法也变成了一个可中断的方法,需要调用者进行处理。
    • 有些情况,不能向上传递异常,比如Thread的run方法,它的声明是固定的,不能抛出任何受检异常,这时,应该捕获异常,进行合适的清理操作,清理后,一般应该调用Thread的interrupt方法设置中断标志位,使得其他代码有办法知道它发生了中断。

BLOCKED

如果线程在等待锁,对线程对象调用interrupt()只是会设置线程的中断标志位,线程依然会处于BLOCKED状态,也就是说,interrupt()并不能使一个在等待锁的线程真正"中断"。
使用synchronized关键字获取锁的过程中不响应中断请求,这是synchronized的局限性。如果这对程序是一个问题,应该使用显式锁。

 

总结

interrupt方法不一定会真正"中断"线程,它只是一种协作机制,如果不明白线程在做什么,不应该贸然的调用线程的interrupt方法,以为这样就能取消线程。
对于以线程提供服务的程序模块而言,它应该封装取消/关闭操作,提供单独的取消/关闭方法给调用者,外部调用者应该调用这些方法而不是直接调用interrupt。

posted @ 2018-08-05 17:43  沉默哥  阅读(3571)  评论(0编辑  收藏  举报