很多人第一次用 interrupt() 时都以为它能像 kill 一样真的把线程停下来。真实构造三种场景,把"中断到底做了什么"这个抽象概念拆成三个具体、可验证的真实结果。
上一篇:真实制造一个死锁,再用 JDK 自带的 API 真实把它揪出来
一个从不检查中断标志位的死循环,调用 interrupt() 只是真实设置了一个标志位,循环本身完全不受影响。
InterruptDemo.java(忙等线程) Thread busy = new Thread(() -> { while (true) { // 从不检查中断标志位 spins.incrementAndGet(); } });
对忙等线程调用 interrupt() 之后,isInterrupted() = true interrupt() 之后 500ms,忙等线程真实还在跑,spins 真实还在涨,当前值 = 176328146 再等 300ms,spins 真实继续涨 = 251938132(真实证明这个线程完全没有被 interrupt() 停下来)
真实结果很直接:isInterrupted() 已经变成 true,证明中断信号确实真实送达了这个线程,但循环计数器完全没有停下来的迹象,800ms 内真实涨了超过 2.5 亿——interrupt() 从头到尾就只是设置了一个内部标志位,不会主动打断线程正在执行的普通代码。
线程如果正阻塞在一小部分特定的方法里(sleep/wait/join 等),interrupt() 会真实让它立刻抛出 InterruptedException。
sleeper 真实被 InterruptedException 提前叫醒,没有等满 60 秒 sleeper 线程真实总耗时 = 205ms(远小于原计划的 60000ms)
同样是调用 interrupt(),这次的真实效果完全不同——线程本来打算睡满 60 秒,真实只睡了约 200ms 就被打断唤醒。这不是巧合,而是 JDK 里少数几个"可中断阻塞方法"(Object.wait()、Thread.sleep()、Thread.join() 等)的共同约定:线程阻塞在这些方法内部时,一旦收到中断信号,会真实提前唤醒并抛出 InterruptedException,同时清除中断标志位。
把"死循环"改成"检查中断标志位就退出的循环",interrupt() 才真实能让它停下来。
InterruptDemo.java(协作式循环) Thread cooperative = new Thread(() -> { while (!Thread.currentThread().isInterrupted()) { // 主动检查 cooperativeSpins.incrementAndGet(); } });
cooperative 线程真实检测到中断标志,主动退出循环 cooperative 线程是否真实已经结束 = true interrupt 前 spins=44917178, 最终 spins=44918404(增长很快就真实停止了)
每一行都是真实运行这段代码得到的结果。