排查死锁一般想到的是 jstack 这类外部工具。其实 JDK 自己就带了一套程序化检测死锁的 API——真实制造一个两线程互相等待的死锁,再用代码把它真实揪出来,不借助任何外部命令行工具。
上一篇:反射调用真实慢多少:真实测出比直接调用慢 6 倍,setAccessible 能省一半
死锁最经典的成因是两个线程以相反的顺序申请同一组锁。用 CountDownLatch 确保两边真实同时拿到第一把锁,再去抢对方手里的锁。
DeadlockDemo.java(核心逻辑) // 线程 1:先拿 lockA,再要 lockB synchronized (lockA) { bothHoldingFirstLock.countDown(); await(bothHoldingFirstLock); // 等线程 2 也真实拿到它的第一把锁 synchronized (lockB) { ... } } // 线程 2:先拿 lockB,再要 lockA synchronized (lockB) { bothHoldingFirstLock.countDown(); await(bothHoldingFirstLock); synchronized (lockA) { ... } }
两个线程各自拿到第一把锁之后,都真实卡在申请第二把锁的地方,谁也等不到对方释放。
线程 1 真实拿到 lockA 线程 2 真实拿到 lockB 线程 2 尝试拿 lockA ... 线程 1 尝试拿 lockB ...从这一刻起,两个线程真实永久卡住——线程 1 拿着 lockA 等 lockB,线程 2 拿着 lockB 等 lockA,谁都不会主动放手
java.lang.management.ThreadMXBean 是 JDK 自带的管理接口,findDeadlockedThreads() 专门用来检测这种循环等待。
DeadlockDemo.java(检测逻辑) ThreadMXBean bean = ManagementFactory.getThreadMXBean(); long[] deadlockedIds = bean.findDeadlockedThreads(); ThreadInfo[] infos = bean.getThreadInfo(deadlockedIds, true, true);
真实检测到 2 个死锁线程: 线程 Thread-A 状态=BLOCKED 正在等待的锁=java.lang.Object@4617c264 该锁真实被谁占着=Thread-B 线程 Thread-B 状态=BLOCKED 正在等待的锁=java.lang.Object@76ed5528 该锁真实被谁占着=Thread-A两条信息真实拼出一个完整的闭环:Thread-A 在等一把 Thread-B 占着的锁,Thread-B 又在等一把 Thread-A 占着的锁——这就是死锁的真实定义,程序自己就能把这个环检测出来并报告清楚
每一行都是真实运行这段代码得到的结果(用CountDownLatch保证死锁真实必然发生)。