Thread.dumpStack()无法定位死锁,因其仅打印单线程调用栈,不采集其他线程状态、锁持有或等待关系;而jstack -l <pid>可生成完整线程快照,明确标识“Found one Java-level deadlock”,并显示waiting to lock与which is held by等关键锁依赖线索。

Thread.dumpStack() 不能定位死锁,jstack 才是唯一可靠的现场诊断工具。它能识别循环等待关系并明确标出“Found 1 deadlock”,而 dumpStack() 只打印单线程调用栈,不分析锁依赖。
为什么 Thread.dumpStack() 对死锁完全无效
死锁的本质是多个线程之间的锁持有/等待关系闭环,不是单个线程卡住。而 Thread.dumpStack() 只做一件事:在当前线程里打印自己的调用栈,不采集其他线程状态,也不检查锁信息。
- 它输出里不会出现
waiting to lock、which is held by这类关键线索 - 即使线程已陷入死锁,
dumpStack()仍只显示类似at java.lang.Object.wait(Native Method),无法区分是正常等待还是死锁等待 - 它不依赖 JVM 的 Attach API,无法穿透到线程锁图(lock graph)层面
jstack -l <pid> 是唯一可信赖的死锁检测入口
必须加 -l 参数,否则锁持有信息会被省略——这是生产环境最常踩的坑。没有 -l,你看到的只是“线程 BLOCKED”,但不知道它在等谁、谁又在等它。
-
jstack -l 12345输出末尾若出现Found one Java-level deadlock:,说明 JVM 已确认循环等待成立 - 下面列出的每个线程行都会包含两个关键字段:
waiting to lock monitor 0x...(想拿的锁)和which is held by "Thread-X"(被谁占着) - 注意
nid=0x...是十六进制本地线程 ID,配合top -H -p 12345可交叉验证是否真有高 CPU 线程在空转(死锁线程通常 CPU 很低)
死锁日志里真正要盯住的三类线索
不要通读整个 thread dump,直接跳到末尾的 deadlock section,再反查涉及线程的完整栈。重点关注:
- 锁对象地址是否一致:比如
object 0x00000007d5a3a0a0在 Thread-0 的 “held” 和 Thread-1 的 “waiting to lock” 中重复出现,说明是同一把锁 - 同步块嵌套顺序:看每个线程的栈顶是否在
synchronized (xxx)内部又尝试进入另一个synchronized (yyy),且两线程顺序相反 - 是否混用实例锁与类锁:例如一个线程持
A.class等new A()实例锁,另一个持实例锁等A.class,这种跨层级锁竞争极难人工发现
复杂系统里死锁往往藏在间接调用链中,比如 A→B→C→synchronized,而另一路 D→E→C→synchronized,表面看不出冲突。这时候必须靠 jstack -l 抽出完整栈+锁归属,人工比对才能还原路径。别指望自动工具包打天下——它只告诉你“有”,不告诉你“怎么绕”。


















