是什么?—— 中断的本质
一个最核心、最需要澄清的观念是:

interrupt() 方法并不会直接“杀死”或强制停止一个正在运行的线程。
Java 中没有一种安全、立即的方法可以强制停止一个线程,强制停止(如已废弃的 stop() 方法)会导致线程在任意点被中断,从而可能破坏对象的不变性状态,导致程序数据不一致。
interrupt() 的本质是:设置一个中断状态位,并向线程发送一个“请求中断”的信号。 它是一种协作机制,需要被中断的线程配合才能完成中断操作。
一个线程是否被中断,完全取决于它自己如何响应这个信号。

中断状态的三个关键方法
Java 中与中断状态相关的有三个核心方法,都定义在 Thread 类中:
| 方法 | 描述 |
|---|---|
void interrupt() |
中断线程。 1. 如果线程处于阻塞状态(例如正在调用 sleep(), wait(), join()),它会立即抛出 InterruptedException 异常,并清除中断状态。 2. 如果线程处于运行/非阻塞状态,该方法只是简单地设置线程的中断状态为 true,线程不会立即停止。 |
boolean isInterrupted() |
检查中断状态。 这是一个实例方法,它会查询线程的中断状态,但不会改变这个状态,如果线程被中断,返回 true,否则返回 false。 |
static boolean interrupted() |
检查当前线程的中断状态,并清除它。 这是一个静态方法,它会查询当前正在执行线程的中断状态,然后无论之前是何状态,都将其中断状态清除(设为 false),注意,它总是作用于当前线程。 |
一个重要的陷阱:
isInterrupted() 和 interrupted() 的区别是初学者最容易混淆的地方,简单记:
thread.isInterrupted(): 问“线程 A 你被中断了吗?” -> 线程 A 回答“是/否”,自己状态不变。Thread.interrupted(): 问“我(当前线程)被中断了吗?” -> 当前线程回答“是/否”,然后立刻擦掉自己的记录(状态设为false)。
怎么做?—— 三种处理中断的场景
根据线程的状态,处理中断的方式分为三种。
线程处于阻塞状态
这是最简单的情况,当线程调用会抛出 InterruptedException 的方法时(如 Thread.sleep(), Object.wait(), Thread.join()),如果它在阻塞期间被中断,JVM 会做两件事:

- 抛出
InterruptedException异常。 - 清除该线程的中断状态。
示例代码:
public class BlockedThread implements Runnable {
@Override
public void run() {
try {
// 线程会在这里阻塞 5 秒
System.out.println("BlockedThread: 我要开始睡觉了...");
Thread.sleep(5000);
System.out.println("BlockedThread: 我睡醒了。");
} catch (InterruptedException e) {
// 捕获到异常,说明在睡眠期间被中断了
System.out.println("BlockedThread: 哎呀,我被吵醒了!(InterruptedException)");
// 在这里可以决定是退出线程还是继续执行
// e.printStackTrace();
}
System.out.println("BlockedThread: 线程结束。");
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new BlockedThread());
t.start();
// 主线程等待 1 秒后,去中断 t 线程
Thread.sleep(1000);
System.out.println("Main: 我要去中断 t 线程了。");
t.interrupt(); // t 线程正在 sleep(),会立即被中断
}
}
输出结果:
BlockedThread: 我要开始睡觉了...
Main: 我要去中断 t 线程了。
BlockedThread: 哎呀,我被吵醒了!(InterruptedException)
BlockedThread: 线程结束。
可以看到,sleep() 被提前终止,异常被抛出。
线程处于运行状态(非阻塞)
这是最常见也最需要手动处理的情况,当线程正在执行自己的任务(例如一个 for 循环)时,你调用了 interrupt(),它只是设置了一个标志位,线程需要主动、定期地检查这个标志位,以决定是否应该停止工作。
最佳实践:使用 isInterrupted() 检查
public class RunningThread implements Runnable {
@Override
public void run() {
try {
// 模拟一个长时间运行的任务
for (int i = 0; i < 100000; i++) {
// 1. 在循环中定期检查中断状态
if (Thread.currentThread().isInterrupted()) {
System.out.println("RunningThread: 收到中断信号,准备退出循环。");
break; // 优雅地退出
}
// 模拟一些工作
doSomeWork();
}
System.out.println("RunningThread: 线程正常结束。");
} finally {
// 清理资源
System.out.println("RunningThread: 清理资源中...");
}
}
private void doSomeWork() {
// 模拟耗时操作
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new RunningThread());
t.start();
// 主线程等待 2 秒后,去中断 t 线程
Thread.sleep(2000);
System.out.println("Main: 我要去中断 t 线程了。");
t.interrupt();
}
}
输出结果:
Main: 我要去中断 t 线程了。
RunningThread: 收到中断信号,准备退出循环。
RunningThread: 线程正常结束。
RunningThread: 清理资源中...
这种方式是最推荐的,因为它不依赖于异常,代码逻辑清晰,能明确地响应中断请求。
线程在调用可中断方法前,中断状态已被设置
这是一个非常微妙但重要的场景,假设一个线程的中断状态已经被设置为 true,然后它去调用一个会抛出 InterruptedException 的方法(sleep())。
会发生什么?
sleep() 方法会检查当前的中断状态,如果发现状态已经是 true,它不会抛出异常,而是直接返回,它会保持中断状态为 true。
示例代码:
public class ScenarioThreeThread implements Runnable {
@Override
public void run() {
// 假设线程的中断状态已经被外部设置为 true
// 我们通过 Thread.interrupted() 来模拟,它会清除状态
// 但为了演示,我们先手动设置
// Thread.currentThread().interrupt(); // 手动设置中断状态为 true
System.out.println("ScenarioThreeThread: 进入 run(),中断状态是 " + Thread.currentThread().isInterrupted());
try {
System.out.println("ScenarioThreeThread: 准备调用 sleep()...");
Thread.sleep(1000); // 检查到中断状态为 true,直接返回,不抛异常
System.out.println("ScenarioThreeThread: sleep() 调用完成。"); // 这行代码不会执行
} catch (InterruptedException e) {
// 这里不会执行,因为 sleep() 没有抛出异常
System.out.println("ScenarioThreeThread: 捕获到 InterruptedException!");
}
System.out.println("ScenarioThreeThread: sleep() 之后,中断状态是 " + Thread.currentThread().isInterrupted());
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new ScenarioThreeThread());
t.start();
// 在 t 线程有机会调用 sleep() 之前,先中断它
t.interrupt();
}
}
输出结果:
ScenarioThreeThread: 进入 run(),中断状态是 true
ScenarioThreeThread: 准备调用 sleep()...
ScenarioThreeThread: sleep() 之后,中断状态是 true
如果你的代码在调用 sleep(), wait() 等方法之前,不确定中断状态是什么,那么最好先处理掉这个中断信号,或者至少意识到它存在,否则,你的代码可能会因为 sleep() 的“静默返回”而产生不符合预期的行为。
最佳实践与总结
-
永远不要使用
Thread.stop():它已废弃,极不安全。 -
响应中断是线程的责任:调用
interrupt()只是发出一个请求,被中断的线程必须在合适的时机检查中断状态并做出响应(通常是退出任务)。 -
优先使用
isInterrupted():在非阻塞代码中,通过while (!Thread.currentThread().isInterrupted())或if (Thread.currentThread().isInterrupted())来检查中断状态,这是最清晰、最健壮的方式。 -
正确处理
InterruptedException:- 传播异常:如果你的方法本身也是一个可中断操作(比如你在一个
run()方法里调用了sleep()),那么最简单的方法就是将InterruptedException抛出去,让调用者去处理。 - 恢复中断状态:如果你捕获了
InterruptedException,但又不能或不想抛出它,你应该在catch块中恢复中断状态,以便上层调用者也能感知到中断请求。try { Thread.sleep(1000); } catch (InterruptedException e) { // 恢复中断状态,以便外层逻辑也能感知 Thread.currentThread().interrupt(); System.out.println("我被中断了,并恢复了这个状态。"); // 或者,如果你只是想记录日志,也可以选择恢复 // e.printStackTrace(); // Thread.currentThread().interrupt(); }
- 传播异常:如果你的方法本身也是一个可中断操作(比如你在一个
-
理解
interrupted()的陷阱:它是一个静态方法,会清除当前线程的中断状态,除非你确实需要这个“检查并清除”的行为,否则在绝大多数情况下,你应该使用实例方法isInterrupted()。 -
中断不等于停止:中断是一个请求,线程可以忽略它(尽管不推荐),或者处理完当前任务后再退出,这给了你极大的灵活性来设计健壮的并发代码。
希望这份详细的解释能帮助你彻底理解 Java 的线程中断机制!
