线程启动以后,如果任务不需要继续执行了,怎么让它停下来?Thread 有一个 interrupt() 方法,但调用它并不是直接结束线程。

中断是一个通知

每个线程都有中断状态。一个线程调用另一个线程的 interrupt(),通常只是把对方的中断状态设为 true,任务需要检查这个状态并决定如何退出。

public class InterruptDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            long count = 0;
            while (!Thread.currentThread().isInterrupted()) {
                count++;
            }
            System.out.println("任务结束");
        }, "worker");
        worker.start();
        worker.interrupt();
        worker.join();
        System.out.println("main 结束");
    }
}

这里不需要先休眠再中断。即使工作线程还没来得及进入循环,中断标记也会在它检查时起作用。输出依次是 任务结束、main 结束,循环执行了多少次则不能确定。

如果把循环条件改成 true,又不做其他中断检查,仅仅调用 interrupt() 不会让这个计算任务退出。

三个方法的区别

方法作用
thread.interrupt()请求中断指定线程
thread.isInterrupted()读取指定线程的中断状态,不清除
Thread.interrupted()读取当前线程的中断状态,并清除

最后一个是静态方法,看的是当前执行线程,而不是某个变量指向的线程。

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

结果是 true、true、false。第二次读取时已经把标记清除了。

阻塞中的线程

线程正在 sleep()、wait() 或 join() 中等待时,收到中断会抛出 InterruptedException,同时清除中断状态。

public class InterruptSleepDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread worker = new Thread(() -> {
            try {
                Thread.sleep(10000);
                System.out.println("正常完成");
            } catch (InterruptedException e) {
                System.out.println("等待被中断");
                Thread.currentThread().interrupt();
            }
        });
        worker.start();
        worker.interrupt();
        worker.join();
    }
}

无论中断发生在 sleep() 之前还是等待期间,这个例子都会进入异常分支。恢复标记后,run() 随即结束。

能向上抛出异常的方法,可以直接声明 throws InterruptedException,交给调用方处理。如果当前层不能抛出,就要决定是结束任务,还是恢复中断标记交给外层判断,不能只打印异常后继续无限循环。

还要区分不同的阻塞方式:等待进入 synchronized 代码块不会因为中断就退出等待;LockSupport.park() 遇到中断会返回,但不按 sleep() 的方式抛出异常并清除标记。中断如何生效,要看具体方法的约定。

最后修改:2018 年 01 月 05 日
如果觉得我的文章对你有用,请随意赞赏