news 2026/4/23 15:26:27

关于 interrupt() 你不可忽视的细节

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
关于 interrupt() 你不可忽视的细节

关于 interrupt() 你不可忽视的细节

引言

在很多开源代码中都会看到Thread.currrentThread().interrupt()这串代码,可是你真的了解它有什么用处吗?今天我们就一起来看看这串代码到底是干嘛的。

以下这串代码是 Spring AI Alibaba 的ModelRetryIntercepter类的片段。

try { log.info("Retry after {} ms", currentDelay); Thread.sleep(currentDelay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Retry interrupted", e); }

消失的信号 – 线程的中断标记位

要理解这个问题,首先要明白 Java 的中断(Interruption)是一种协作机制,而不是强制命令。它依赖于线程对象内部的一个布尔值标记位(InterruptStatus)。

当我们调用t.interrupt()时,仅仅是把这个标记位设为true

然而,Java 的阻塞库函数(如Thread.sleep()Object.wait()BlockingQueue.take())对中断非常敏感。它们的处理逻辑通常包含以下两个原子步骤:

  1. 检测到中断信号:发现中断标记位为true
  2. 清除标记并抛出异常:为了响应这个中断,它们会先将标记位重置为 false,然后抛出InterruptedException

这就是问题的核心:当你捕获到InterruptedException时,线程的中断标记位已经被 JVM 自动重置为false了。

如果你在catch块中什么都不做(Swallow the exception),或者只是简单打印日志,那么线程看起来就像是从未被中断过一样。

结果 – “僵尸线程”

让我们看一个典型的生产环境 Bug。假设你需要在一个线程中不断处理任务,直到线程池关闭:

publicvoidrun(){// 依靠中断状态来判断是否结束while(!Thread.currentThread().isInterrupted()){try{// 模拟执行任务doWork();// 模拟阻塞Thread.sleep(1000);}catch(InterruptedExceptione){// 错误做法:只是打印日志,没有恢复状态System.out.println("收到停止信号,但我把信号搞丢了...");}}System.out.println("线程退出");}

发生了什么?

  1. 外部调用future.cancel(true)executor.shutdownNow(),发送中断信号。
  2. sleep()此时被唤醒,它清除标志位(变回false),抛出异常。
  3. 进入catch块,打印日志。
  4. 代码继续运行,回到while循环头部。
  5. 检查!isInterrupted(),由于刚才标志位被清除了,这里返回true(即认为没有被中断)。
  6. 线程继续死循环执行,无法停止。

这就是所谓的僵尸线程——你以为你把它杀死了,但它只是“震动”了一下(抛了个异常),然后像没事人一样继续跑。

正确的姿势 – 恢复现场

为了解决这个问题,我们需要在捕获异常后,手动恢复刚才丢失的中断状态。

publicvoidrun(){while(!Thread.currentThread().isInterrupted()){try{doWork();Thread.sleep(1000);}catch(InterruptedExceptione){System.out.println("被中断,准备退出...");// 正确做法:重新设置中断状态Thread.currentThread().interrupt();// 通常配合 break 跳出循环break;}}}

这行代码的作用就是告诉后续的代码(包括while判断,或者调用栈上层的代码):“刚才发生过中断,虽然异常把标志位清除了,但我现在把它补回来。”

那你可能有疑问?为什么我不直接抛出异常?你可以抛出异常,但是分场景来。

  • 可以抛出时: 如果你的方法签名允许抛出(例如你写的是普通业务方法),那优先选择抛出异常,这是最正确的做法,让上层决定如何处理。
  • 不能抛出时: 在实现Runnable.run()接口时,run方法的签名是固定的,不允许抛出受检异常。你必须在内部try-catch。在这种情况下,你捕获了异常,就必须负责恢复状态,以便调用栈上层的代码能感知到“哦,原来有人想让我停止”

时序问题

我们通过一段代码来验证我们所说的问题,体验瞬间的变化:

Threadt=newThread(()->{try{System.out.println("1. 准备睡觉");Thread.sleep(5000);}catch(InterruptedExceptione){// 当我们进入这里时,JVM 已经把标志位清除了System.out.println("3. 捕获异常,此时标志位是: "+Thread.currentThread().isInterrupted());// 输出 false// 所以我们需要手动“恢复”Thread.currentThread().interrupt();System.out.println("4. 手动恢复后,标志位是: "+Thread.currentThread().isInterrupted());// 输出 true}});t.start();Thread.sleep(100);// 确保子线程已经睡着了System.out.println("2. 主线程调用 interrupt");t.interrupt();// 这一瞬间,t 的标志位其实是 true,但很快就被 sleep 内部逻辑清除了

假设线程 A 正在Thread.sleep(10000)

  1. Step 1:其他线程动作其他线程调用threadA.interrupt()

    • 结果: 此时,线程 A 的中断标志位瞬间变为true
  2. Step 2:线程 A (JVM 内部机制) 响应线程 A 处于sleep状态,JVM 内部机制检测到了标志位变成了true

  3. Step 3:清除状态 (关键步骤)JVM 决定唤醒线程 A。但在唤醒并抛出异常之前,JVM 会做一个动作:将中断标志位重置(清除)为false

    • 为什么要清除?就像你设定了一个闹钟,闹钟响了(抛出异常),你醒来后第一件事是按下闹钟的停止键(清除标志),否则它会一直响。
  4. Step 4: 抛出异常 JVM 抛出 InterruptedException,控制权进入你的catch块。

    • 结果: 当代码运行到catch块里面时,你如果去查isInterrupted(),看到的就是false

总结

永远不要生吞(Swallow)中断异常,除非你明确知道你的线程设计就是为了忽略停止信号

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/22 11:48:48

MediaPipe Pose实战指南:瑜伽动作评估系统搭建

MediaPipe Pose实战指南:瑜伽动作评估系统搭建 1. 引言 1.1 AI 人体骨骼关键点检测的兴起 随着计算机视觉技术的快速发展,人体姿态估计(Human Pose Estimation)已成为智能健身、运动康复、虚拟试衣和人机交互等领域的核心技术之…

作者头像 李华
网站建设 2026/4/15 6:28:20

彩虹骨骼效果惊艳!MediaPipe Hands手势追踪案例展示

彩虹骨骼效果惊艳!MediaPipe Hands手势追踪案例展示 1. 引言:从指尖到交互的智能感知革命 在人机交互日益追求自然化、直觉化的今天,手势识别技术正成为连接人类动作与数字世界的桥梁。传统的触控、语音交互虽已成熟,但在特定场…

作者头像 李华
网站建设 2026/4/22 16:22:10

AI人体骨骼检测环境部署:Python包集成免下载配置教程

AI人体骨骼检测环境部署:Python包集成免下载配置教程 1. 引言 1.1 AI 人体骨骼关键点检测的应用价值 随着人工智能在计算机视觉领域的深入发展,人体姿态估计(Human Pose Estimation)已成为智能健身、动作捕捉、虚拟现实、安防监…

作者头像 李华
网站建设 2026/4/23 11:40:41

DeepSeek-Prover-V1:AI数学证明准确率46.3%创标杆

DeepSeek-Prover-V1:AI数学证明准确率46.3%创标杆 【免费下载链接】DeepSeek-Prover-V1 通过大规模合成数据,DeepSeek-Prover-V1 提升了语言模型在定理证明领域的表现,翻译数学竞赛题目生成 Lean 4 证明数据,实现 46.3% 整证生成准…

作者头像 李华
网站建设 2026/4/23 11:42:03

MediaPipe Pose调优指南:光照变化下的检测稳定性提升

MediaPipe Pose调优指南:光照变化下的检测稳定性提升 1. 引言:挑战与价值 1.1 光照变化带来的现实挑战 在实际应用中,AI人体骨骼关键点检测常面临复杂多变的环境条件,其中光照不均、明暗对比强烈或低照度场景是影响模型稳定性的…

作者头像 李华
网站建设 2026/4/23 11:41:51

通俗解释触发器在MySQL和PG中的应用场景

触发器的真正价值:从MySQL到PostgreSQL,如何用数据库自动响应数据变化?你有没有遇到过这样的场景?运维同事直接连上生产数据库删了一条记录,结果没人知道是谁、什么时候删的;多个微服务都在改同一张表&…

作者头像 李华