如何中断线程

张贤 2020年03月14日 121次浏览

先来说以下已经被抛弃的用于中断线程的方法:

  • 通过调用stop()方法停止线程
    这种方法是不安全的。比如线程 A 调用线程 B 的stop()方法去停止 B 线程,但是 线程 A 是不知道线程 B 的执行情况的,这种突然的停止会导致线程 B 的一些清理工作还没完成就被强制停止了,而且线程 B 会立刻释放所占有的锁,这有可能会引发数据不同步的问题

  • 通过调用suspend()方法和resume()方法
    这种方法也被抛弃了

目前推荐使用的方法是调用interrupt()方法,这种方法不是立刻中断线程,而是通知线程应该中断了,而被通知的线程何时中断,则由被通知的线程自己来处理。

  • 如果线程处于被阻塞状态,那么线程将立刻推出被阻塞状态,并抛出一个 InterruptedException
  • 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,被设置中断标志的线程将继续正常运行,不受影响

因此,只使用interrupt()方法不能中断线程,需要被调用的线程配合中断

  • 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
  • 如果线程处于正常活动状态,那么会将该线程的中断表示设置为 true,被设置中断标志的线程将正常运行,不受影
public class InterruptedDemo {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = new Runnable() {
            @Override
            public void run() {
                int i = 0;
                try {
                    //在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
                    while (!Thread.currentThread().isInterrupted()) {
                        //子线程休眠 100 ms,这时如果主线程调用子线程的 interrupt() 方法,子线程会抛出异常。
                        Thread.sleep(100);
                        i++;
                        System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getState()+") loop "+i);
                    }
                } catch (InterruptedException e) {
                    //在调用阻塞方法时正确处理 InterruptedException 异常。(例如,catch 到异常后就终止线程)
                    System.out.println(Thread.currentThread().getName()+"("+Thread.currentThread().getState()+") catch  InterruptedException");
                }
            }
        };

        Thread t1=new Thread(task,"t1");
        System.out.println(t1.getName()+"("+t1.getState()+") is New");

        t1.start();
        System.out.println(t1.getName()+"("+t1.getState()+") is started");

        //主线程休眠 300 ms,然后给 t1 发送中断指令
        Thread.sleep(300);
        t1.interrupt();
        System.out.println(t1.getName()+"("+t1.getState()+") is interrupted");

        //主线程休眠 300 ms,然后查看 t1 的状态
        Thread.sleep(300);
        System.out.println(t1.getName()+"("+t1.getState()+") is interrupted now");
    }
}

上述代码的输出是:

t1(NEW) is New
t1(RUNNABLE) is started
t1(RUNNABLE) loop 1
t1(RUNNABLE) loop 2
t1(TIMED_WAITING) is interrupted
t1(RUNNABLE) catch  InterruptedException
t1(TERMINATED) is interrupted now

线程休眠 100 ms,这时如果主线程调用子线程的 interrupt() 方法,子线程会抛出异常。子线程捕获到InterruptedException后,又会进入RUNNABLE状态。