当前位置: 首页 > news >正文

死锁检测 及其测试用例

1)实现

package com.next1b.nextsvr.util.deadlockcheck;import com.next1b.nextsvr.util.log.Log;import java.lang.management.*;
import java.util.HashMap;
import java.util.Map;/*** 查找死锁,打印死锁上下文*/
public final class DeadLockCheckThread extends Thread {private final static int MAX_DEPTH = 255;// 最大检测等待时间private static final int MaxCheckPeriodMs = 5000;// 当前线程是否还在运行private volatile boolean running = true;// jmx线程对象private final ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();// 多久检测一次死锁private int sleepIdleMs = 1000;public DeadLockCheckThread() {super("DeadLockCheckThread");// 守护线程this.setDaemon(true);}@Overridepublic void run() {while (running) {try {// 检测到了死锁if (checkDeadLock()) { // 核心方法,检测死锁sleepIdleMs = 1000; // 检测到了,则改为下次1s后再进行下次检测} else {sleepIdleMs += 1000; // 没检测到,则加大下次检测的时间,就不要那么频繁了if (sleepIdleMs > MaxCheckPeriodMs) {sleepIdleMs = MaxCheckPeriodMs;}}// 休眠一会sleepIdle(sleepIdleMs);} catch (Throwable e) {Log.logException(e);}}}/*** 关闭*/public void shutdown() {running = false;}/*** 检测死锁*/private boolean checkDeadLock() {// step1:返回死锁的线程Id列表long[] deadlockedThreadIds = threadMXBean.findDeadlockedThreads();if (null == deadlockedThreadIds) {return false; // 说明没找到死锁}// step2:走到这里,说明找到了死锁// 构建死锁线程信息映射 <threadId, ThreadInfo>Map<Long, ThreadInfo> deadLockedThreadId2ThreadInfoMap = new HashMap<>();ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(deadlockedThreadIds, threadMXBean.isObjectMonitorUsageSupported(), threadMXBean.isSynchronizerUsageSupported());for (ThreadInfo threadInfo : threadInfos) {try {if (null != threadInfo&& threadInfo.getLockOwnerId() != -1) { // getLockOwnerId == -1,不在等待被其他线程拥有的锁. 肯定不是环的一部分。deadLockedThreadId2ThreadInfoMap.put(threadInfo.getThreadId(), threadInfo);}} catch (Exception e) {Log.logException(e);}}// step3:检测死锁环,打印下死锁信息while (!deadLockedThreadId2ThreadInfoMap.isEmpty()) {// 检查死锁Map<Long, ThreadInfo> threadId2ThreadInfoCycleMap = new HashMap<>();// 先拿到第一个线程信息ThreadInfo threadInfo = deadLockedThreadId2ThreadInfoMap.entrySet().iterator().next().getValue();do {if (null != threadId2ThreadInfoCycleMap.put(threadInfo.getThreadId(), threadInfo)) { // 环找到了,说明有死锁了// 打印发现的死锁信息StringBuilder sb = new StringBuilder();// 打印的时候把挂在环上的线程也打出来。for (ThreadInfo info : threadId2ThreadInfoCycleMap.values()) {fillThreadInfo(info, sb);}Log.logErr("\n----------DEAD LOCK BEGIN----------\n" + sb + "----------DEADLOCK END----------");// 打断当前的do while循环,执行外层的while循环并尝试寻找下一个死锁.break;}} while ((threadInfo = deadLockedThreadId2ThreadInfoMap.get(threadInfo.getLockOwnerId())) != null);// 删除已经被处理的线程。cycle是完整的环,或者是那些等待的环已被打破剩下的孤立枝节。deadLockedThreadId2ThreadInfoMap.keySet().removeAll(threadId2ThreadInfoCycleMap.keySet());}return true;}/*** @param threadInfo 死锁环中线程的信息* @param sb 线程信息*/private void fillThreadInfo(ThreadInfo threadInfo, StringBuilder sb) {// 当前死锁的线程信息sb.append("ThreadName=").append(threadInfo.getThreadName()).append(" ThreadId=").append( threadInfo.getThreadId()).append(" ThreadState=").append(threadInfo.getThreadState());String lockOwnerStr = "";if (threadInfo.getLockOwnerName() != null) {lockOwnerStr = threadInfo.getLockOwnerName() + "\" ThreadId=" + threadInfo.getLockOwnerId();}if (threadInfo.isSuspended()) {sb.append(" (suspended)");}if (threadInfo.isInNative()) {sb.append(" (in native)");}sb.append('\n');StackTraceElement[] stackTrace = threadInfo.getStackTrace();int i = 0;for (; i < stackTrace.length && i < MAX_DEPTH; i++) {for (MonitorInfo mi : threadInfo.getLockedMonitors()) {if (mi.getLockedStackDepth() == i) {sb.append("ERROR\t- SuccessLock:"); // 当前拿到的锁的信息sb.append(mi);sb.append('\n');}}if (i == 0 && threadInfo.getLockInfo() != null) {State ts = threadInfo.getThreadState();switch (ts) {case BLOCKED:sb.append("ERROR\t- FailLock:"); // 当前无法获得的锁信息sb.append(getLockOwnerStr(threadInfo, lockOwnerStr));sb.append('\n');break;case WAITING:sb.append("ERROR\t- waiting on:");sb.append(getLockOwnerStr(threadInfo, lockOwnerStr));sb.append('\n');break;case TIMED_WAITING:sb.append("ERROR\t- timed waiting on:");sb.append(getLockOwnerStr(threadInfo, lockOwnerStr));sb.append('\n');break;default:}}StackTraceElement ste = stackTrace[i];sb.append("ERROR\t- ").append(ste.toString()).append("\n"); // 发现死锁的代码位置}if (i < stackTrace.length) {sb.append("ERROR\t...");sb.append('\n');}// 获取当前获得的重入锁LockInfo[] locks = threadInfo.getLockedSynchronizers();if (locks.length > 0) {sb.append("ERROR\n\tNumber of LockedSynchronizer = ").append(locks.length);sb.append('\n');for (LockInfo li : locks) {sb.append("ERROR\t- ").append(li);sb.append('\n');}}sb.append("\n\n");}private String getLockOwnerStr(ThreadInfo  threadInfo, String lockOwnerStr) {return threadInfo.getLockInfo() + " CurOwnerThread:" + lockOwnerStr; // 当前持有的线程}/*** 挂起当前线程一段时间。*/private synchronized void sleepIdle(long ms) {try {this.wait(ms);} catch (Exception e) {Log.logException(e);}}
}

2)测试用例

package com.example.variable;import com.example.variable.deadlock.DeadLockCheckThread;
import com.example.variable.deadlock.Student;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;@SpringBootTest
class DeadLockCheckTests {private final DeadLockCheckThread deadLockCheckThread = new DeadLockCheckThread();@BeforeEachvoid setUp() {deadLockCheckThread.start();}@Testpublic void test() throws Exception {Object lock1 = new Object();Student lock2 = new Student();new Thread(()->{synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (lock2){System.out.println("thread1 run");}}}, "thread1").start();new Thread(()->{synchronized (lock2){try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (lock1){System.out.println("thread2 run");}}}, "thread2").start();Thread.sleep(Long.MAX_VALUE);}}

3)效果

假设线程1和线程2相互争夺出现了死锁,打印结果如下:

1 // 打印了就说明发现了死锁
2 // 死锁的线程1
3 // 当前死锁线程1已经持有的锁
4 // 当前死锁线程1想要竞争但是没有竞争到的锁 +  这个没有竞争到的锁当前被哪个线程2持有
5 // 死锁线程1的代码出现在哪一行
6 // 哪个线程2持有的这个锁

http://www.xdnf.cn/news/1392427.html

相关文章:

  • 地铁隧道病害智能巡检系统——机器视觉技术的深度应用
  • Idea2025.2 MybatisX插件失效问题
  • vue3+wangEditor实现富文本编辑器
  • cursor的setting設置換行
  • 命令拓展(草稿)
  • Vue开发准备
  • Silvaco TCAD | Victory DoE的基本使用方法(三)
  • nacos单机部署并开启鉴权
  • 2025.8.29机械臂实战项目
  • Windows 下 MSYS2 + MinGW-w64 配置 Fyne GUI 编译环境全流程
  • Redis-分布式缓存
  • Java深拷贝与浅拷贝核心解析
  • 设计模式:装饰模式(Decorator Pattern)
  • Kubernetes 与 GitOps 的深度融合实践指南
  • 【3D入门-指标篇上】3D 网格重建评估指标详解与通俗比喻
  • 3D 数字孪生可视化技术在学校项目中的应用
  • “破译”的密钥/算法类型
  • 【工具】开源大屏设计器 自用整理
  • LeetCode第二题知识点2 ---- 栈、堆、地址
  • LeetCode - 128. 最长连续序列
  • Vue3+Ant-design-vue 实现树形穿梭框
  • BlueKing-ci
  • 币安创始人赵长鹏:香港需要更广泛的加密货币产品来与美国和阿联酋竞争
  • docker-相关笔记
  • Cesium 入门教程(十三):粒子系统实例
  • 2025年03月 Scratch 图形化(一级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • springboot中循环依赖的解决方法-使用反射
  • mysql双机热备(主主模式)
  • Java项目实现【记录系统操作日志】功能
  • 基于FPGA的DDR3读写实验学习