排查 synchronized 死锁需先观察卡顿、低 CPU 等现象,再用 jps 和加 -l 参数的 jstack 定位闭环等待链;关键看线程互相等待不同锁地址,结合代码验证加锁顺序不一致;还可借助 jconsole、Arthas thread -b 或 VisualVM 辅助分析。

排查 synchronized 引起的死锁和线程阻塞,核心是定位“谁在等谁的锁”以及“谁一直没释放锁”。Java 自带工具链足够高效,无需额外依赖,关键在操作顺序和结果解读。
快速确认是否真发生了死锁
先观察现象再动手分析:程序卡住、无响应、接口超时但 CPU 使用率偏低(常低于 10%),这些是典型信号。如果只是个别线程 BLOCKED,不等于死锁;只有多个线程形成闭环等待时,才是真正的死锁。
- 用 jps -l 查出目标 Java 进程 PID(例如 12345)
- 执行 jstack -l 12345 > deadlock.log —— 必须加 -l 参数,否则看不到锁对象地址和持有关系
- 打开日志,直接翻到文件末尾,搜索 found one Java-level deadlock
看懂 jstack 输出的关键区块
死锁区块会清晰列出参与线程、锁地址、等待链和源码行号。例如:
- "Thread-0" is waiting to lock <0x00000000d5f6e3a0>, which is held by "Thread-1"
- "Thread-1" is waiting to lock <0x00000000d5f6e3b8>, which is held by "Thread-0"
- 紧接着的 stack trace 显示:at com.example.DeadLockTest.lambda$main$0(DeadLockTest.java:17) —— 这就是 synchronized(lockA) 那一行
两个锁地址不同,说明是两个独立对象;两个线程互相等待对方持有的锁,循环依赖成立。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
对照代码验证加锁顺序冲突
死锁本质不是“用了 synchronized”,而是“多把锁的获取顺序不一致”。重点检查:
- 线程 A 是否先 synchronized(lockA),再 synchronized(lockB)
- 线程 B 是否先 synchronized(lockB),再 synchronized(lockA)
- 注意锁对象类型:synchronized(this)、synchronized(静态类)、synchronized(new Object()) 都算不同锁,都可能卷入同一死锁链
- 跨方法调用也要追踪——比如 methodA() 拿了 lock1,调用 methodB() 又试图拿 lock2,而另一线程反向操作,同样构成风险
补充:其他实用排查方式
除了 jstack,还有更轻量或更直观的选择:
- jconsole:启动后连接进程 → 切到“线程”页 → 点击“检测死锁”,自动弹出死锁线程列表和依赖图
- Arthas thread -b:在生产环境无图形界面时特别好用,一条命令直接标出阻塞源头
- VisualVM / JMC:适合持续监控,能发现反复出现的 BLOCKED 状态趋势,提前预警潜在死锁苗头

















