ThreadMXBean 提供最轻量标准的 synchronized 死锁检测方式,通过 findDeadlockedThreads() 判断是否存在 JVM 级死锁,并结合 getThreadInfo 获取锁细节与堆栈定位问题。

synchronized 锁本身不提供运行时状态暴露能力,但 JVM 在底层会为每个 monitor(即 synchronized 锁)维护持有线程、等待队列等元数据。ThreadMXBean 正是通过读取这些 JVM 内置信息,来检测由 synchronized 引发的死锁——无需修改任何 synchronized 代码块,也无需加日志或埋点。
直接调用 findDeadlockedThreads() 判断是否存在死锁
这是最轻量、最标准的方式。synchronized 和 ReentrantLock 都会被识别,只要满足循环等待条件:
- 获取 ThreadMXBean 实例:ThreadMXBean bean = ManagementFactory.getThreadMXBean();
- 执行检测:long[] ids = bean.findDeadlockedThreads();
- 返回 null 或长度为 0 的数组 → 当前无 JVM 级死锁
- 返回非空数组 → 至少存在一组互相阻塞的线程,且至少有一把锁是 synchronized monitor
结合 getThreadInfo 获取锁细节和堆栈定位问题
仅知道线程 ID 不够,必须补全上下文才能定位到哪段 synchronized 代码出了问题:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 调用 bean.getThreadInfo(ids, true, true):第二个 true 表示包含锁信息(如 locked synchronizers),第三个 true 表示包含完整堆栈
- 遍历每个 ThreadInfo,重点关注:
- info.getLockName():返回类似 "java.lang.Object@12345678" 的字符串,对应 synchronized(obj) 中的 obj
- info.getLockedSynchronizers():对 synchronized 锁返回 null(因它不是 LockInfo 类型),但对 ReentrantLock 会返回具体锁实例
- info.getStackTrace():查找 monitorenter 指令所在行,或方法入口处的 synchronized 块起始位置(如 UserService.update() line 45)
注意 synchronized 死锁的典型堆栈特征
JVM 对 synchronized 阻塞的线程状态标记很明确,看 jstack 或 ThreadInfo 堆栈时要认准这些信号:
立即学习“Java免费学习笔记(深入)”;
- 线程状态为 BLOCKED (on object monitor),且堆栈首帧含 at java.lang.Object.wait(Native Method) 或直接停在 synchronized 方法/代码块内
- 堆栈中出现 waiting to lock <0x...> —— 这个地址就是它想进的 synchronized 块所对应的对象
- 另一线程堆栈中出现 locked <0x...>,且该地址与前者 waiting 的地址一致 → 形成闭环
- 不要误判 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject,那是 await(),不是 synchronized 阻塞
生产环境建议的集成方式
别等服务卡死才查,把检测变成可触发、可监控的动作:
- 封装为 Spring Boot Actuator 自定义 endpoint,例如 /actuator/deadlock-check,运维可随时 curl 触发
- 用 ScheduledExecutorService 每 10 秒轮询一次,连续 3 次非空再告警(避免瞬时竞争误报)
- 在关键服务启动完成、压测结束、或定时巡检任务中主动调用,记录日志并带上时间戳和 JVM PID
- 配合 jstack -l 输出做交叉验证:ThreadMXBean 发现死锁后,立刻执行 jstack -l
保存原始现场

















