【问题标题】:Programmatic deadlock detection in javajava中的程序化死锁检测
【发布时间】:2010-11-09 07:07:19
【问题描述】:

我如何以编程方式检测 Java 程序中发生了死锁?

【问题讨论】:

  • 您应该指定是否需要以编程方式或通过某种外部监控工具来检测?
  • JavaSpecialist 网站(总是值得一读)对此有一个interesting article,讨论理论和实践。
  • 啊,我的回答是假设使用工具。我想无论如何了解这两种方法都是有用的。

标签: java multithreading concurrency deadlock


【解决方案1】:

您可能需要考虑IBM's MTRAT。毕竟预防胜于治疗。 Multicore Software Development Kit 还自带死锁检测工具。

【讨论】:

  • “MTRAT”链接转到有关浮点运算的页面。对那是什么感到困惑
【解决方案2】:

如果您希望它在运行时完成,您可以使用 watchdog

【讨论】:

    【解决方案3】:

    一个有用的调查提示:

    如果您可以当场发现应用程序并怀疑发生了死锁,请在 java.exe 控制台窗口中按“Ctrl-Break”(或在 Solaris/Linux 上按“Ctrl-\”)。 jvm会转储所有线程的当前状态和堆栈跟踪,找出死锁并精确描述它们。

    它看起来像这样:

    Full thread dump Java HotSpot(TM) Client VM (1.5.0_09-b03 mixed mode):
    
    "[Test Timer] Request Queue" prio=6 tid=0x13d708d0 nid=0x1ec in Object.
        wait() [0x1b00f000..0x1b00fb68]
        at java.lang.Object.wait(Native Method)
        at java.lang.Object.wait(Unknown Source)
        at library.util.AsyncQueue.run(AsyncQueue.java:138)
            - locked <0x02e70000> (a test.server.scheduler.SchedulerRequestQueue)
    
        ...
    
    Found one Java-level deadlock:
    =============================
    "Corba service":
      waiting to lock monitor 0x13c06684 (object 0x04697d90, a java.lang.Object),
      which is held by "[Server Connection] Heartbeat Timer"
    "[Server Connection] Heartbeat Timer":
      waiting to lock monitor 0x13c065c4 (object 0x0467e728, a test.proxy.ServerProxy), which is held by "Corba service"
    
    Java stack information for the threads listed above:
    ===================================================
    "Corba service":
        at test.proxy.ServerProxy.stopHBWatchDog(ServerProxy:695)
        - waiting to lock <0x04697d90> (a java.lang.Object)
        ...
    

    【讨论】:

    • 或 Solaris/Linux 中的 ctrl-\。
    • 实际上您想要的是发送一个“QUIT”信号(即 SIGQUIT),但它并不是真正的“QUIT”。 Java 应用程序上的 OS X、Linux(和 Solaris)上的 SIGQUIT 会转储堆栈跟踪。 Ctrl+\ 是发送 SIGQUIT 的一种方式。获取 pid 并执行:kill -3 {id} 是另一种方法。
    【解决方案4】:

    如果您不需要编程检测,您可以通过 JConsole 执行此操作;在线程选项卡上有一个“检测死锁”按钮。在 JDK6 中,这会检测内部监视器和 j.u.c Locks 的锁

    通过 $JAVA_HOM/bin/jconsole 命令运行 JConsole

    【讨论】:

      【解决方案5】:

      您可以使用 JDK 附带的 ThreadMXBean 以编程方式执行此操作:

      ThreadMXBean bean = ManagementFactory.getThreadMXBean();
      long[] threadIds = bean.findDeadlockedThreads(); // Returns null if no threads are deadlocked.
      
      if (threadIds != null) {
          ThreadInfo[] infos = bean.getThreadInfo(threadIds);
      
          for (ThreadInfo info : infos) {
              StackTraceElement[] stack = info.getStackTrace();
              // Log or store stack trace information.
          }
      }
      

      显然,您应该尝试隔离正在执行此死锁检查的线程 - 否则如果该线程死锁,它将无法运行检查!

      顺便说一句,这就是 JConsole 在幕后使用的。

      【讨论】:

      • 该方法的 Javadoc 说它可能很昂贵。你知道有多贵吗?
      • 不,我不知道,但我猜你可以编写一些代码来计时操作,看看它如何随着线程数/锁数的增加而扩展。
      • 这段代码有一些问题。首先,findDeadlockedThreads 可能会为某些线程返回 null,这可能会导致循环中出现 NPE。其次,这不会打印实际的堆栈跟踪,您需要从线程信息中获取实际的线程。您可以查看此帖子以获取更多信息:korhner.github.io/java/multithreading/…
      • @korhner: findDeadlockedThreads() 将在没有死锁线程的情况下返回 null,我已经检查过了;我不相信它会返回一个包含零星空值的数组。如果相应的线程不活动,getThreadInfo 可以返回一个包含空值的数组。但是,我已经知道他们在上一次通话中还活着(并且陷入僵局)。回覆。第二点我将堆栈跟踪记录作为 OP 的练习,但他们实际上并没有提到他们想要这样做。
      • @Adamski:findDeadlockedThreads 可以报告死锁,即使线程在实际上不是真正的 dedlock 的锁上超时(在释放超时锁之后)。理论上,线程可能会在超时之后和 getThreadInfo 之前被销毁。你是对的 findDeadlockedThreads 不会为特定线程返回 null 但是,对不起。
      【解决方案6】:

      tempus-fugit 还与编程线程转储类一起实现它。它是使用上面提到的 mbean 机制实现的,并提供了一种即插即用的超级骗子解决方案。

      【讨论】:

        【解决方案7】:

        这里有代码:http://www.java2s.com/Code/Java/Development-Class/PerformingdeadlockdetectionprogrammaticallywithintheapplicationusingthejavalangmanagementAPI.htm

        奇迹发生在ThreadMonitor.findDeadlock():

          public boolean findDeadlock() {
            long[] tids;
            if (findDeadlocksMethodName.equals("findDeadlockedThreads")
                && tmbean.isSynchronizerUsageSupported()) {
              tids = tmbean.findDeadlockedThreads();
              if (tids == null) {
                return false;
              }
        
              System.out.println("Deadlock found :-");
              ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true);
              for (ThreadInfo ti : infos) {
                printThreadInfo(ti);
                printLockInfo(ti.getLockedSynchronizers());
                System.out.println();
              }
            } else {
              tids = tmbean.findMonitorDeadlockedThreads();
              if (tids == null) {
                return false;
              }
              ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
              for (ThreadInfo ti : infos) {
                // print thread information
                printThreadInfo(ti);
              }
            }
        
            return true;
          }
        

        这会调用ThreadMXBean 的 API,该 API 在 Java 5 和 6 中具有不同的名称(因此是外部的 if())。

        代码示例还允许中断锁,因此您甚至可以打破死锁。

        【讨论】:

          【解决方案8】:

          JArmus 是一个用于检测和避免死锁的库。它包括支持: Thread.joinCyclicBarrierCountDownLatchPhaserReentrantLock.

          要使用 JArmus,您需要检测您的代码。通过其检测类之一或自动使用 JArmus 仪器jarmusc

          java -jar jarmusc.jar yourprogram.jar checkedprogram.jar

          输入yourprogram.jar 是您要检查的程序。 输出是同一个程序,通过检查自动查找任何死锁。

          障碍需要一些帮助

          使用类CyclicBarrierCountDownLatchPhaser 验证死锁有点棘手——例如,JConsole 无法检测这些类型的死锁。 JArmus 需要您提供一点帮助:您必须指定哪些线程正在影响同步,我们将这些线程称为已注册 线程。

          线程必须尽快将自己标记为已注册。标记已注册线程的好地方是开头方法Runnable.run JArmus.register(latch);

          示例

          下面的死锁程序被正确识别 贾姆斯:

          final CountDownLatch latch = new CountDownLatch(2);
          final CyclicBarrier barrier = new CyclicBarrier(2);
          final Queue<Exception> exceptions = new ArrayDeque<>();
          Thread t1 = new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      JArmus.register(barrier); // do not forget to register!
                      JArmus.register(latch); // do not forget to register!
                      latch.countDown();
                      latch.await();
                      barrier.await();
                  } catch (Exception e) {
                      exceptions.add(e);
                  }
              }
          });
          Thread t2 = new Thread(new Runnable() {
              @Override
              public void run() {
                  try {
                      JArmus.register(barrier); // do not forget to register!
                      JArmus.register(latch); // do not forget to register!
                      barrier.await();
                      latch.countDown();
                      latch.await();
                  } catch (Exception e) {
                      exceptions.add(e);
                  }
              }
          });
          t1.start();
          t2.start();
          

          【讨论】:

            【解决方案9】:

            您可以使用 ThreadMXBean 类以编程方式检测死锁线程。这里是代码,

                ThreadMXBean bean = ManagementFactory.getThreadMXBean();
            
                long ids[] = bean.findMonitorDeadlockedThreads();
            
                if(ids != null)
                {
                    ThreadInfo threadInfo[] = bean.getThreadInfo(ids);
            
                    for (ThreadInfo threadInfo1 : threadInfo)
                    {
                        System.out.println(threadInfo1.getThreadId());    //Prints the ID of deadlocked thread
            
                        System.out.println(threadInfo1.getThreadName());  //Prints the name of deadlocked thread
            
                        System.out.println(threadInfo1.getLockName());    //Prints the string representation of an object for which thread has entered into deadlock.
            
                        System.out.println(threadInfo1.getLockOwnerId());  //Prints the ID of thread which currently owns the object lock
            
                        System.out.println(threadInfo1.getLockOwnerName());  //Prints name of the thread which currently owns the object lock.
                    }
                }
                else
                {
                    System.out.println("No Deadlocked Threads");
                }
            

            点击here了解更多关于如何检测死锁线程的信息。

            【讨论】:

            • 最好使用 findDeadlockedThreads() 而不是 findMonitorDeadlockedThreads()。 findDeadlockedThreads 已在 1.6 中引入,与 findMonitorDeadlockedThreads() 相比更好,后者可能会丢失一些死锁情况而不报告它们。它无法报告以下死锁:ReentrankLock 或 ReentrantReadWriteLock。更多信息可以在这里找到:meteatamel.wordpress.com/2012/03/21/deadlock-detection-in-java 在“findMonitorDeadlockedThreads 与 findDeadlockedThreads”标题下
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-04-26
            • 2014-07-19
            • 1970-01-01
            • 2017-09-23
            • 2011-12-28
            • 1970-01-01
            相关资源
            最近更新 更多