Java 的线程中断是一种协作请求,不是强制终止。调用者发出请求,执行线程按自身控制流响应并清理资源;如果线程从不检查中断,或阻塞操作不响应它,请求就不一定及时结束工作。
三个名字相近的方法
| 方法 | 含义 |
|---|---|
| target.interrupt() | 向目标线程发出中断请求 |
| target.isInterrupted() | 读取目标线程的中断状态,不清除 |
| Thread.interrupted() | 检查并清除当前线程的中断状态 |
Thread.interrupted 是静态方法。通过某个线程变量调用它,也不会变成检测那个目标线程。循环里如果只想检测而不消费标记,通常使用当前线程的 isInterrupted。Java 8 Thread
不要使用已废弃的 Thread.stop 强制终止工作。它可能在不变量尚未恢复时释放监视器,造成其他线程观察到不一致状态。
让运行代码真正走到退出路径
以下 Java 8 示例用 CountDownLatch 协调“线程已经启动”,不依赖主线程猜测等待几秒:
import java.util.concurrent.CountDownLatch;
public class InterruptExitDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch started = new CountDownLatch(1);
Thread worker = new Thread(() -> {
try {
started.countDown();
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(100); // 用等待代表一个有界工作步骤
} catch (InterruptedException error) {
Thread.currentThread().interrupt();
return;
}
}
} finally {
System.out.println("执行资源清理");
}
});
worker.start();
started.await();
worker.interrupt();
worker.join();
}
}
sleep 抛出 InterruptedException 时会清除中断状态。这里恢复标记并离开当前任务,而不是打印堆栈后继续无限循环。无论请求发生在条件检查还是等待阶段,finally 都是清理路径。
示例中的 join 等待线程退出;真实应用应根据关闭时限决定是否使用有界等待,并处理等待者自身被中断的情况。
break 不等于离开 run
break 只跳出当前循环,循环之后的代码仍会继续执行。return 离开当前方法;若它就在 run 内,run 将结束。也可以让一个声明 InterruptedException 的业务方法向上传播,再由任务边界决定退出。
不需要为了停止而刻意制造异常;关键是取消信号有没有到达应该停止的边界。无法向上传播、但上层仍需识别中断时,恢复标记通常比吞掉它更合适。
中断不替代所有取消机制
wait、sleep、join 有相应中断语义,但不能推广为所有网络、文件或第三方调用都会立刻终止。业务还可能需要关闭资源、超时、取消令牌和远端补偿。
即使停止请求与正常完成竞争,也应只进行一次必要清理;取消本地线程不代表已发生的远端操作自动回滚。