线程启动以后,如果任务不需要继续执行了,怎么让它停下来?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() 的方式抛出异常并清除标记。中断如何生效,要看具体方法的约定。