死锁排查实战:JVM 唯一会“自动报案“的问题(场景 B5)

环境:CentOS 7 / JDK 17 / 4 核 8G

演示程序:jvm-tuning-demo(SlowResponseDemo deadlock 场景:两线程交叉持锁)

系列:CPU 100% / 锁竞争 / 频繁 Young GC / 切换风暴 / 下游慢 / 池耗尽

关键词:Found one Java-level deadlock、循环等待、持锁顺序、tryLock

一、事故现场:不是慢,是"死"

某功能完全无响应------不是延迟几秒,是请求挂到天荒地老。CPU 反而很低(线程全卡死了,没人干活)。

死锁 vs 锁竞争的体感区别:

锁竞争(B1) 死锁(B5)
现象 慢,但请求最终能完成 彻底卡死,永不返回
本质 排队,总会轮到 循环等待,永远等不到
可恢复性 流量降了自己缓过来 只能重启

二、实锤:jstack 拉到最后

死锁是所有 JVM 问题里唯一不用自己分析的 ------jstack 会自动检测并在报告末尾给出"尸检报告":

bash 复制代码
jstack <PID> | tail -30
复制代码
Found one Java-level deadlock:
=============================
"Thread-1":
  waiting to lock monitor 0x... (object 0x000000008d71ce98, a java.lang.Object),
  which is held by "Thread-2"
"Thread-2":
  waiting to lock monitor 0x... (object 0x000000008d71ce88, a java.lang.Object),
  which is held by "Thread-1"

Java stack information for the threads listed above:
"Thread-1":
        at ...DeadLock.lambda$main$0(SlowResponseDemo.java:150)
        - waiting to lock <0x...ce98>
        - locked <0x...ce88>          ← 拿着 ce88,等 ce98
"Thread-2":
        at ...DeadLock.lambda$main$1(SlowResponseDemo.java:163)
        - waiting to lock <0x...ce88>
        - locked <0x...ce98>          ← 拿着 ce98,等 ce88
Found 1 deadlock.

读法 :每个线程两行------- locked 是它手里攥着的,- waiting to lock 是它伸手要的。两个线程互相伸手要对方攥着的,环就闭合了。

三、死锁四条件(面试必背,破坏任一即解)

  1. 互斥:锁是独占的
  2. 持有并等待:拿着 A 不放,去要 B ← 本案剧情
  3. 不可剥夺:锁不能被抢走
  4. 循环等待:线程 1→2→1 形成等待环(jstack 检测的就是这个环)

对应代码:

java 复制代码
// 线程1:lock1 → lock2          // 线程2:lock2 → lock1(顺序相反 = 找死)
synchronized (lock1) {            synchronized (lock2) {
    synchronized (lock2) {...}        synchronized (lock1) {...}
}                                 }

四、调整方案

方案 破坏的条件 说明
统一全局持锁顺序 循环等待 所有线程都按 lock1→lock2 顺序申请,环无法形成。首选,成本最低
tryLock(timeout) 超时放弃 持有并等待 拿不到就松手重试(ReentrantLock),synchronized 做不到这点
消除嵌套锁 持有并等待 能一把锁解决就别套两层;能无锁(CAS)就别上锁
死锁监控 --- 运维侧用 jstack 定时巡检 + ThreadMXBean.findDeadlockedThreads() 做告警埋点

五、排查命令(最简单的一篇)

bash 复制代码
jstack <PID> | tail -30          # JVM 自动汇总 deadlock 报告(jstack -l 效果相同)
jcmd <PID> Thread.print | tail -30   # 等价替代

面试话术:"死锁不用分析,jstack 拉到最后 JVM 自己报告------哪个线程拿哪把锁、等哪把锁、卡在哪一行,全写着。我们要做的是修持锁顺序,让等待环无法闭合。"
口诀:卡死拉到最后看,locked 对 waiting 连成环;统一顺序加超时,环形等待不再现。

相关推荐
daidaidaiyu9 小时前
ThingsBoard 集群的核心逻辑源码分析
java
我可能是个假开发10 小时前
FTP Unicode 文件名导致 `550` 的问题分析与解决方案
java·开发语言·spring
caoerzhong10 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·python·开源
【JAVA】玩家11 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
Wang's Blog12 小时前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
MandalaO_O13 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
xiaoqiMikko13 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
狼爷14 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言
念何架构之路14 小时前
zap扩展生态与总结
java·前端·数据库
第七页独白15 小时前
汽车零件厂如何通过 QMS 真正落地 IATF 16949——QMS软件系统:品质检验-内审稽核-8d客诉管理:全星质量管理软件系统
java·前端·数据库