面试知识库
进阶

线程中断机制#

一句话答案#

中断是一种协作式的取消信号:interrupt() 只是把目标线程的中断标志置位,线程自己通过检查标志或捕获 InterruptedException 来决定如何响应,而不是被强制杀死。

核心要点

三个核心方法(极易混):

方法作用是否清除标志
t.interrupt()(实例方法)给线程 t 发中断信号(置位中断标志)
t.isInterrupted()(实例方法)查询线程 t 是否被中断
Thread.interrupted()静态方法)查询当前线程是否被中断是(会清除)

两种响应中断的方式:

1. 阻塞方法会抛 InterruptedException

  • sleep() / wait() / join() / BlockingQueue.take() 等阻塞方法,被中断时会抛 InterruptedException
  • 关键陷阱:抛出异常的同时会清除中断标志(标志位复位为 false)
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // 此时中断标志已被清除!如果上层还需要知道被中断过,必须重新置位
    Thread.currentThread().interrupt();   // 恢复中断状态
}
java

2. 非阻塞循环主动轮询标志

while (!Thread.currentThread().isInterrupted()) {
    // 干活,定期检查中断标志,被中断就退出循环
}
java

正确的”优雅停止线程”姿势:

为什么废弃 stop() / suspend()

  • stop() 会立刻释放所有锁并强行终止,可能让对象处于不一致的中间状态(数据损坏)
  • suspend() 挂起时不释放锁,极易死锁
  • 正确做法:用中断(或 volatile 标志位)协作式停止

volatile 标志位 vs 中断:

  • volatile boolean running 标志位简单,但无法唤醒正在阻塞的线程(线程卡在 sleep/wait 里看不到标志)
  • 中断能同时打断阻塞 + 通知退出,更通用——所以涉及阻塞操作时优先用中断
面试回答(2分钟版)

Java 的线程中断是一种协作式的取消机制,不是强制杀线程。interrupt() 只是把目标线程的中断标志位置为 true,线程要不要停、怎么停由它自己决定。响应中断有两种方式:一种是 sleep、wait、join、阻塞队列的 take 这些阻塞方法,被中断时会抛 InterruptedException,这里有个大坑——抛异常的同时中断标志会被清除,所以如果上层还需要知道被中断过,要在 catch 里重新调用 interrupt() 把标志恢复回去;另一种是非阻塞的循环里主动轮询 isInterrupted(),发现被中断就退出。要注意区分 isInterrupted() 是实例方法不清除标志,而 Thread.interrupted() 是静态方法查的是当前线程并且会清除标志。优雅停止线程应该用中断或者 volatile 标志位协作式地停,而不是用已经废弃的 stop(),因为 stop() 会立刻释放锁强行终止,可能让数据处于不一致状态。volatile 标志位的缺点是唤不醒正在阻塞的线程,所以涉及阻塞操作时优先用中断。

追问与易错

追问方向:

  • “interrupt() 会立刻停止线程吗?”→ 不会,只是置位标志,线程自己决定如何响应
  • “InterruptedException 捕获后中断标志还在吗?”→ 不在了,抛异常时会清除,需手动 interrupt() 恢复
  • “isInterrupted() 和 Thread.interrupted() 区别?”→ 前者实例方法不清标志,后者静态方法查当前线程且清标志
  • “为什么 Thread.stop() 被废弃?”→ 强行终止+立即释放锁,可能导致数据不一致和资源未释放
  • “怎么优雅地关闭线程池?”→ shutdown() 不再接新任务并等已有任务跑完,shutdownNow() 给工作线程发 interrupt

易错点:

  • ❌ “catch 到 InterruptedException 直接吞掉就行”——会丢失中断信息,上层无法感知,应恢复标志或向上抛
  • ❌ “用 volatile 标志位一定能停下线程”——线程卡在 sleep/wait 时看不到标志,停不下来
  • ❌ “interrupt() 能中断 IO”——传统阻塞 IO 不响应中断(除非是 InterruptibleChannel/NIO)