关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »如何通过编程发现Java死锁

    如何通过编程发现Java死锁

    2015-03-20 00:00:00 出处:oschina
    分享

    死锁是指,两个或多个动作一直在等待其他动作完成而使得所有动作都始终处在阻塞的状态。想要在开发阶段检测到死锁是非常困难的,而想要解除死锁往往需要重新启动程序。更糟的是,死锁通常发生在负载最重的生产过程中,而想要在测试中发现它,十分不易。之所以这么说,是因为测试线程之间所有可能的交叉是不现实的。尽管出现了一些静态分析库可以帮助我们发现可能出现的死锁,我们还是有必要在运行时检测到死锁,并且得到有用的信息,以便我们解决这个问题或者重启程序,或者做些其他的事情。

    在编程中使用ThreadMXBean类来检测死锁

    Java 5引入了ThreadMXBean接口,它提供了多种监视线程的方法。我建议您了解所有这些方法,因为当您没使用外部工具时,它们会为您提供很多有用的操作以便您监测程序性能。这里,我们感兴趣的方法是findMonitorDeadlockedThreads,如过您使用的是Java 6,对应的方法是findDeadlockedThreads。二者的区别的是,findDeadlockedThreads还可以检测到owner locks(java.util.concurrent)引起的死锁,而findMonitorDeadlockedThreads只能检测monitor locks(例如,同步块)。由于保留老版本的方法只是出于兼容性的考虑,所以我将使用新版本的方法。在这里,编程的思想是把对死锁的周期性检测封装到一个可重用组件里,之后我们只需启动它、随它去。

    一种实现调度的方法是通过执行器框架,即一组良好抽象并易于使用的多线程类。

    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    this.scheduler.scheduleAtFixedRate(deadlockCheck, period, period, unit);

    就是那么简单,在我们通过选择周期和时间单位而设置了一个特定时间后,就得到了一个周期性调用的线程。接着,我们想使功用得以拓展从而允许用户提供在程序检测到死锁时所触发的行为。最后,我们需要一个方法来接收用于描述死锁中所有线程的一系列对象。

    void handleDeadlock(final ThreadInfo[] deadlockedThreads);

    现在,实现死锁检测类已经万事俱备了。

    public interface DeadlockHandler {
      void handleDeadlock(final ThreadInfo[] deadlockedThreads);
    }
    
    public class DeadlockDetector {
    
      private final DeadlockHandler deadlockHandler;
      private final long period;
      private final TimeUnit unit;
      private final ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
      private final ScheduledExecutorService scheduler = 
      Executors.newScheduledThreadPool(1);
    
      final Runnable deadlockCheck = new Runnable() {
        @Override
        public void run() {
          long[] deadlockedThreadIds = DeadlockDetector.this.mbean.findDeadlockedThreads();
    
          if (deadlockedThreadIds != null) {
            ThreadInfo[] threadInfos = 
            DeadlockDetector.this.mbean.getThreadInfo(deadlockedThreadIds);
    
            DeadlockDetector.this.deadlockHandler.handleDeadlock(threadInfos);
          }
        }
      };
    
      public DeadlockDetector(final DeadlockHandler deadlockHandler, 
        final long period, final TimeUnit unit) {
        this.deadlockHandler = deadlockHandler;
        this.period = period;
        this.unit = unit;
      }
    
      public void start() {
        this.scheduler.scheduleAtFixedRate(
        this.deadlockCheck, this.period, this.period, this.unit);
      }
    }

    让我们动手试试。首先,我们要创建一个handler用来向System.err输出死锁线程的信息。在现实场景中,我们可以用它发送邮件,比如:

    public class DeadlockConsoleHandler implements DeadlockHandler {
    
      @Override
      public void handleDeadlock(final ThreadInfo[] deadlockedThreads) {
        if (deadlockedThreads != null) {
          System.err.println("Deadlock detected!");
    
          Map<Thread, StackTraceElement[]> stackTraceMap = Thread.getAllStackTraces();
          for (ThreadInfo threadInfo : deadlockedThreads) {
    
            if (threadInfo != null) {
    
              for (Thread thread : Thread.getAllStackTraces().keySet()) {
    
                if (thread.getId() == threadInfo.getThreadId()) {
                  System.err.println(threadInfo.toString().trim());
    
                  for (StackTraceElement ste : thread.getStackTrace()) {
                      System.err.println("t" + ste.toString().trim());
                  }
                }
              }
            }
          }
        }
      }
    }

    这一过程在所有的堆栈追踪中反复进行并为每个线程信息打印对应的堆栈踪迹。通过这种方式,我们可以准确知道每个线程等待的位置和对象。但这个方法有一个缺陷——当一个线程只是暂时等待时,可能会被当作一个暂时的死锁,从而引发错误的警报。出于此,当我们处理死锁时,原始线程不能继续存在而findDeadlockedThreads方法会返回没有此类线程。为了避免可能出现的NullPointerException,我们需要警惕这种情况。最后,让我们促成一个死锁来看看系统是如何运行的。

    DeadlockDetector deadlockDetector = new DeadlockDetector(new DeadlockConsoleHandler(), 5, TimeUnit.SECONDS);
    deadlockDetector.start();
    
    final Object lock1 = new Object();
    final Object lock2 = new Object();
    
    Thread thread1 = new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (lock1) {
          System.out.println("Thread1 acquired lock1");
          try {
            TimeUnit.MILLISECONDS.sleep(500);
          } catch (InterruptedException ignore) {
          }
          synchronized (lock2) {
            System.out.println("Thread1 acquired lock2");
          }
        }
      }
    
    });
    thread1.start();
    
    Thread thread2 = new Thread(new Runnable() {
      @Override
      public void run() {
        synchronized (lock2) {
          System.out.println("Thread2 acquired lock2");
          synchronized (lock1) {
            System.out.println("Thread2 acquired lock1");
          }
        }
      }
    });
    thread2.start();

    输出:

    Thread1 acquired lock1
    Thread2 acquired lock2
    Deadlock detected!
    “Thread-1” Id=11 BLOCKED on java.lang.Object@68ab95e6 owned by “Thread-0” Id=10
    deadlock.DeadlockTester$2.run(DeadlockTester.java:42) 
     java.lang.Thread.run(Thread.java:662)
    “Thread-0” Id=10 BLOCKED on java.lang.Object@58fe64b9 owned by “Thread-1” Id=11
     deadlock.DeadlockTester$1.run(DeadlockTester.java:28)
     java.lang.Thread.run(Thread.java:662)

    记住,死锁检测的开销可能会很大,你需要用你的程序来测试一下你是否真的需要死锁检测以及多久检测一次。我建议死锁检测的时间间隔至少为几分钟,因为更加频繁的检测并没有太大的意义,原因是我们并没有一个复原计划,我们能做的只是调试和处理错误或者重启程序并祈祷不会再次发生死锁。如果你有关于解决死锁问题的好建议或者关于这个解决方案的疑问,请在下面留言。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    最全面的前端开发指南

    Windows7任务栏桌面下角的一些正在运行的图标不见了

    sql server快速删除记录方法

    SQL Server 7移动数据的6种方法

    SQL Server 2008的新压缩特性

    每个Java程序员必须知道的5个JVM命令行标志

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。