65

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

4

9 回答 9

68

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

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 在幕后使用的东西。

于 2009-07-09T07:49:40.740 回答
14

一个有用的调查提示:

如果您可以当场抓住应用程序并怀疑发生了死锁,请在 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)
    ...
于 2009-07-09T07:43:34.040 回答
7

您可以使用 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");
    }

单击此处以获取有关如何检测死锁线程的更多信息。

于 2015-03-09T14:28:56.640 回答
3

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

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

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

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

障碍需要一些帮助

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

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

例子

JArmus 正确识别了以下死锁程序:

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();
于 2014-05-05T15:06:35.300 回答
2

您可能需要考虑IBM 的 MTRAT。毕竟预防胜于治疗。多核软件开发工具包还带有死锁检测工具。

于 2009-07-09T07:32:12.503 回答
2

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

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

于 2009-07-09T07:48:20.943 回答
1

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

于 2010-01-02T17:37:02.010 回答
1

这里有代码: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在 Java 5 和 6 中具有不同名称的 API(因此称为 outer if())。

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

于 2011-11-16T10:15:39.033 回答
0

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

于 2009-07-09T07:34:53.810 回答