用jstack定位synchronized死锁的核心是执行jstack -l pid > deadlock.log捕获带锁快照,搜索“found one java-level deadlock”识别线程间locked与waiting to lock地址构成的闭环,并结合堆栈中at *.java:行号定位synchronized代码位置。

用 jstack 定位 synchronized 引起的死锁,核心是捕获带锁信息的线程快照,再识别“互相等待对方持有的锁”这一闭环结构。整个过程不改代码、不重启服务,几分钟就能锁定问题源头。
拿到 Java 进程 PID
先确认目标应用正在运行,再获取它的进程 ID:
- 执行
jps -l,列出所有 Java 进程及其主类或 JAR 路径 - 找到你的应用对应行,例如
12345 com.example.TransferService,其中12345就是 PID - 若在 Docker 容器中,可进容器后执行;权限不足时加
sudo或切换到应用用户
生成带锁详情的线程快照
-l 参数必不可少,否则看不到 synchronized 锁对象的持有关系:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 运行命令:
jstack -l 12345 > deadlock.log - 如果进程已卡死无响应,仍能成功执行——
jstack读取的是 JVM 内存状态,不依赖业务逻辑继续运行 - 生产环境建议连续抓 3 次(如间隔 2 秒),避免因时机偏差漏掉死锁态
快速定位死锁区块和关键线索
打开 deadlock.log,直接搜索关键词:
- 找
Found one Java-level deadlock:—— 这是 JVM 自动检测出的明确信号,通常在文件开头或末尾 - 下方会列出参与死锁的线程名,例如:
"Thread-A": waiting to lock (a java.lang.Object), which is held by "Thread-B""Thread-B": waiting to lock (a java.lang.Object), which is held by "Thread-A" - 比对两个
地址:不同地址 + 互相持有/等待 = 典型synchronized死锁闭环
顺着堆栈找到具体代码行
死锁摘要后面紧跟着每个线程的完整调用栈:
- 查找类似
at com.example.TransferAccount.transfer(TransferAccount.java:23)的行,这就是synchronized块所在位置 - 看该行是否为
synchronized(this)、synchronized(target)或synchronized(new Object())等形式 - 结合两个线程各自持有的锁地址,回到源码确认:线程 A 是否先锁账户 A 再锁账户 B,线程 B 是否反向操作
只要抓取时机得当、关注锁地址与堆栈上下文,jstack -l 定位 synchronized 死锁并不复杂,但容易因忽略 -l 参数或只抓一次而错过关键线索。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










