当前位置: 首页 > 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.dtcms.com/a/357552.html

相关文章:

  • Linux的环境变量
  • 10迁移TiDB数据库数据到GaussDB
  • Java实现PDF表格转换为CSV
  • 消失的数字
  • RAG 系统核心:深入理解向量相似度匹配与文本向量化
  • Python中将py程序打包成exe可执行文件并封装环境以及本地依赖+单个exe和文件目录两种状态(亲测可用建议收藏)
  • docker 下载镜像报错
  • [HFCTF2020]EasyLogin
  • 日本IT|C++相关面试及问答技巧分享
  • STM32项目分享:基于单片机的自行车测速系统设计
  • Win11 压缩实测:Win11 的压缩软件的最佳配置和使用方式
  • 网站酷炫换皮肤?——PC 端 H5 换肤方案实战分享
  • WebGIS开发智慧校园(8)地图控件
  • A股大盘数据-20250829 分析
  • 03.《交换的底层逻辑:从基础到应用》
  • vue3中安装tailwindcss
  • ​​字节跳动重磅开源 Seed-OSS 大模型系列,12T tokens训练,原生支持512K长上下文​
  • python 2025/7/28
  • 【完整源码+数据集+部署教程】工地建筑进度监测系统源码和数据集:改进yolo11-SDI
  • 【笔记】扩散模型(一二)U-ViT|Diffusion with Transformer
  • 智慧园区系统:基于Java微服务架构与全栈信创国产化的数字化赋能平台
  • 人工智能一些基础概念与应用场景学习笔记
  • C++基础(③反转字符串(字符串 + 双指针))
  • solidity地址、智能合约、交易概念
  • Pointer--Learing MOOC-C语言第九周指针
  • 鸿蒙地址选择库(ArkTs UI)
  • Idea2025.2 MybatisX插件失效问题
  • Suno-API - OpenI
  • 【计算机网络】前端基础知识Cookie、localStorage、sessionStorage 以及 Token
  • 04.《VLAN基础与配置实践指南》