用jstack排查Java死锁的核心是“抓现场→看输出→找循环”:先用jps -l查PID,再用jstack -F -l PID > dump.txt导出带锁详情的线程快照;接着在dump.txt末尾搜索Found one Java-level deadlock,分析waiting to lock和which is held by确定阻塞链;然后回溯各线程堆栈定位synchronized或Lock.lock()代码行,比对锁对象hashcode,检查加锁顺序是否不一致;最后可通过连续两次jstack对比BLOCKED线程堆栈是否固定来验证。

用 jstack 排查 Java 多线程死锁,核心是“抓现场→看输出→找循环”,不依赖猜测,靠工具直接暴露阻塞链。
第一步:快速获取进程 ID 和线程快照
先确认目标 Java 进程的 PID:
- 执行 jps -l,找到你的应用类名对应的数字(比如 12345 com.example.MyApp)
- 用 jstack -l 12345 > dump.txt 导出带锁详情的线程快照
(-l 很关键,它会显示谁持有哪把锁、谁在等哪把锁) - 若进程已卡死无响应,可加 -F 强制抓取:jstack -F -l 12345 > dump.txt
第二步:定位死锁摘要和线程关系
打开 dump.txt,直接拉到文件末尾搜索关键词:
- 查找 Found one Java-level deadlock: —— 如果存在,jstack 会自动标出全部死锁线程
- 下面会列出类似结构:
"Thread-1": waiting to lock <0x00000007d5a3a0a0> (a java.lang.Object), which is held by "Thread-0"
"Thread-0": waiting to lock <0x00000007d5a3a0d0> (a java.lang.Object), which is held by "Thread-1" - 注意两个关键字段:
waiting to lock → 当前线程想拿但拿不到的锁
locked 或 which is held by → 它自己拿着的锁,正被另一个线程等着
第三步:回溯代码定位根因
光知道“谁等谁”还不够,得找到具体哪段代码在加锁:
立即学习“Java免费学习笔记(深入)”;
- 在 dump.txt 中找到对应线程的完整堆栈(比如 "Thread-0" 的 Java stack information 部分)
- 看最后一行或倒数几行的 at xxx.xxx.method(XXX.java:23),定位到 synchronized 块或 Lock.lock() 调用位置
- 重点比对多个死锁线程持有的锁对象 identity hashcode(如 <0x00000007d5a3a0a0>),在代码里搜这些锁的声明位置
- 检查是否出现加锁顺序不一致:例如一个方法先锁 A 再锁 B,另一个却先锁 B 再锁 A
第四步:验证与辅助技巧
单次 dump 有时不够,可结合以下方式增强判断:
- 连续执行两次 jstack(间隔 2–3 秒),对比 BLOCKED 线程的堆栈是否始终停在同一个 synchronized 或 lock() 调用上
- 关注线程状态为 BLOCKED 且堆栈中含 waiting to lock 的条目,它们大概率是死锁参与者
- 若用的是 Collections.synchronizedMap 等包装类,注意它的锁就是 map 实例本身,避免外部再用 synchronized(map) 造成重复竞争


















