InterruptedException 是线程协作信号而非错误,捕获后必须调用 Thread.currentThread().interrupt() 恢复中断状态,否则中断语义失效;阻塞方法响应中断,计算循环需手动轮询 isInterrupted()。

InterruptedException 不是异常,是线程协作的信号
Java 中 InterruptedException 不代表程序出错了,而是 JVM 在调用 Thread.sleep()、Object.wait()、BlockingQueue.take() 等阻塞方法时,检测到当前线程被其他线程调用了 interrupt(),于是主动抛出该异常来“唤醒”它。忽略或吞掉这个异常,等于屏蔽了中断请求,线程将无法被及时停止,破坏中断语义。
捕获后必须恢复中断状态,不能只 catch 了事
标准做法是:在 catch (InterruptedException e) 块中,立即调用 Thread.currentThread().interrupt() 恢复中断标志位。否则上层调用者(比如框架或容器)将收不到中断信号,导致超时控制、任务取消等机制失效。
public void run() {
while (!Thread.currentThread().isInterrupted()) {
try {
doWork();
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // ✅ 关键:恢复中断
break;
}
}
}
- 不要写
e.printStackTrace()或空 catch —— 这是最常见误用 - 不要在 catch 后继续循环而不检查中断状态 —— 可能无限重试
- 如果方法签名不允许抛出
InterruptedException(如实现了Runnable.run()),必须恢复中断,不能转成RuntimeException包装后扔出去(除非你明确知道上层会处理)
使用 interrupt() 时要注意目标线程是否处于可中断状态
调用 thread.interrupt() 只有在目标线程正阻塞于可中断方法(如 sleep、wait、join、LockSupport.park())时才会立即触发 InterruptedException;若线程正在执行普通代码(比如密集计算),则只会设置中断标志位 isInterrupted() == true,需手动轮询检查。
- 纯计算型循环必须显式判断:
if (Thread.currentThread().isInterrupted()) break; -
Thread.interrupted()是静态方法,会清除中断状态;isInterrupted()是实例方法,不改变状态 —— 一般优先用后者 - 使用
ExecutorService.shutdownNow()会尝试对所有运行中的任务线程调用interrupt(),但效果取决于任务自身是否响应中断
第三方库和 JDK 类的中断行为差异要查文档
不是所有阻塞操作都响应中断。例如:
立即学习“Java免费学习笔记(深入)”;
-
InputStream.read()在 socket 未就绪时会阻塞,但默认不响应中断(除非底层是InterruptibleChannel,如SocketChannel) -
ReentrantLock.lock()不响应中断;必须用lockInterruptibly() -
CountDownLatch.await()、Semaphore.acquire()都响应中断 - Lombok 的
@SneakyThrows会自动吞掉InterruptedException,极易引发中断丢失 —— 生产环境禁用
关键点在于:中断安全不是“加个 try-catch 就完事”,而是整个执行路径上每一处阻塞点都要确认是否可中断、是否恢复了中断状态、是否在非阻塞段做了轮询。漏掉任意一环,中断就可能静默失效。










