JVM不会自动检测并解除死锁,但可通过jstack -l 自动发现死锁并打印闭环等待关系:如Thread-1持有A锁等待B锁,Thread-2持有B锁等待A锁;再结合日志中waiting to lock/locked地址及源码行号定位加锁顺序冲突。

Java 中发生死锁时,JVM 会自动检测并打印详细的死锁日志(前提是使用了 jstack 或启用了 -XX:+PrintConcurrentLocks 等相关机制),关键是要从日志中快速定位到**相互等待的两个(或多个)线程**及其持有的锁与等待的锁。核心思路是:找「持有某锁却在等另一个锁」+「对方也持有后者却在等前者」的闭环。
看线程状态和锁等待关系
死锁日志里每个线程块都会标明状态(如 java.lang.Thread.State: BLOCKED (on object monitor)),重点看后面括号里的内容:
- “waiting to lock ” → 该线程正试图获取这个对象监视器(即 synchronized 锁)但被阻塞
- “- locked ” → 该线程已成功持有这个锁(注意:可能有多行 “locked”,表示持有多把锁)
例如:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f8b4c0a2000 nid=0x3e6a waiting for monitor entry [0x00007f8b3d9f9000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadlockExample.methodA(DeadlockExample.java:15)
- waiting to lock (a java.lang.Object)
- locked (a java.lang.Object)
<p>"Thread-2" #13 prio=5 os_prio=0 tid=0x00007f8b4c0a3000 nid=0x3e6b waiting for monitor entry [0x00007f8b3d8f8000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadlockExample.methodB(DeadlockExample.java:25)</p>
- waiting to lock (a java.lang.Object)
- locked (a java.lang.Object)
这里就能看出:Thread-1 持有 0x...3c50 却等待 0x...3c40;Thread-2 持有 0x...3c40 却等待 0x...3c50 → 构成循环等待,确认死锁。
对照源码定位加锁顺序
仅靠地址不够,要结合日志中的行号(如 DeadlockExample.java:15)和方法名,回到代码看 synchronized 块或 synchronized(this) 的实际对象:
- 检查 methodA 中先锁了 obj2(对应
0x...3c50),再尝试锁 obj1(0x...3c40) - 检查 methodB 中先锁了 obj1(
0x...3c40),再尝试锁 obj2(0x...3c50)
两段逻辑加锁顺序相反,是典型死锁成因。
注意可重入锁和 ReentrantLock 的差异
如果是 ReentrantLock(非 synchronized),日志中不会出现 “waiting to lock ”,而是类似:
- parking to wait for (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
同时会有 Locked ownable synchronizers: 区域,列出该线程当前持有的 ReentrantLock 实例地址。仍可用相同方式比对:一个线程持有 A 锁、等待 B 锁;另一个持有 B 锁、等待 A 锁。
用 jstack 自动检测死锁(最省力)
直接运行:jstack -l
JVM 在检测到死锁时,会在日志开头明确标出:
Found one Java-level deadlock: ============================= "Thread-1": waiting for ownable synchronizer 0x000000071a2b3c40, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-2" "Thread-2": waiting for ownable synchronizer 0x000000071a2b3c50, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-1"
这段就是结论性输出,无需手动推导,直接锁定两个线程和互持的锁。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











