Java中的死锁检测

时间:2008-10-19 22:54:21

标签: java multithreading deadlock

很久以前,我从Java参考书中保存了一句话:“Java没有处理死锁的机制。它甚至不知道发生了死锁。” (Head First Java 2nd Edition,p.516)

那么,它是什么?有没有办法在Java中捕获死锁案例?我的意思是,有没有办法让我们的代码理解发生了死锁的情况?

16 个答案:

答案 0 :(得分:78)

从JDK 1.5开始,java.lang.management包中有非常有用的方法来查找和检查发生的死锁。请参阅findMonitorDeadlockedThreads()类的findDeadlockedThreads()ThreadMXBean方法。

使用此方法的一种可能方法是使用单独的监视程序线程(或定期任务)执行此操作。

示例代码:

  ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
  long[] ids = tmx.findDeadlockedThreads();
  if (ids != null) {
     ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
     System.out.println("The following threads are deadlocked:");
     for (ThreadInfo ti : infos) {
        System.out.println(ti);
     }
  }

答案 1 :(得分:18)

JConsole能够检测正在运行的应用程序中的死锁。

答案 2 :(得分:11)

JDK 5和6将把保存的锁信息转储到完整的线程转储中(使用kill -3,jstack,jconsole等获得)。 JDK 6甚至包含有关ReentrantLock和ReentrantReadWriteLock的信息。从这些信息中可以通过找到锁定周期来诊断死锁:线程A保持锁定1,线程B保持锁定2,并且A正在请求2或B正在请求1.根据我的经验,这通常是非常明显的。

其他分析工具实际上可以找到潜在的死锁,即使它们没有发生。来自OptimizeIt,JProbe,Coverity等供应商的线程工具是很好看的地方。

答案 3 :(得分:8)

请注意,使用并发包非常难以调试的死锁类型。这就是你有一个ReentrantReadWriteLock并且一个线程抓住读锁定然后(比方说)试图进入一个其他线程持有的监视器,该线程也在等待抓取写锁定。使调试特别困难的原因是没有人输入读锁的记录。这只是一个计数。该线程甚至可能抛出异常并死亡,使读取计数不为零。

以下是前面提到的findDeadlockedThreads方法无法获取的示例死锁:

import java.util.concurrent.locks.*;
import java.lang.management.*;

public class LockTest {

    static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public static void main(String[] args) throws Exception {
        Reader reader = new Reader();
        Writer writer = new Writer();
        sleep(10);
        System.out.println("finding deadlocked threads");
        ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
        long[] ids = tmx.findDeadlockedThreads();
        if (ids != null) {
            ThreadInfo[] infos = tmx.getThreadInfo(ids, true, true);
            System.out.println("the following threads are deadlocked:");
            for (ThreadInfo ti : infos) {
                System.out.println(ti);
            }
        }
        System.out.println("finished finding deadlocked threads");
    }

    static void sleep(int seconds) {
        try {
            Thread.currentThread().sleep(seconds*1000);
        } catch (InterruptedException e) {}
    }

    static class Reader implements Runnable {
        Reader() {
            new Thread(this).start();
        }
        public void run() {
            sleep(2);
            System.out.println("reader thread getting lock");
            lock.readLock().lock();
            System.out.println("reader thread got lock");
            synchronized (lock) {
                System.out.println("reader thread inside monitor!");
                lock.readLock().unlock();
            }
        }
    }

    static class Writer implements Runnable {
        Writer() {
            new Thread(this).start();
        }
        public void run() {
            synchronized (lock) {
                sleep(4);
                System.out.println("writer thread getting lock");
                lock.writeLock().lock();
                System.out.println("writer thread got lock!");
            }
        }
    }
}

答案 4 :(得分:6)

通常,java不提供死锁检测。 synchronized锁定和内置监视器使得解决死锁的难度比使用显式锁定的语言更难。

我建议迁移到使用java.util.concurrent.Lock锁等,以使您的锁定方案更容易推理。实际上,您可以轻松地使用死锁检测自行实现锁定接口。该算法基本上遍历锁依赖图并寻找一个循环。

答案 5 :(得分:4)

如果遵循一个简单的规则,就可以避免死锁:让所有线程声明并以相同的顺序释放它们的锁。通过这种方式,您永远不会遇到可能发生死锁的情况。

即使是餐饮哲学家的问题也可以被视为违反这一规则,因为它使用左右勺子的相对概念,这导致不同的线程使用不同的勺子分配顺序。如果勺子的编号是唯一的,并且哲学家们都试图先得到编号最低的勺子,那么僵局就不可能了。

在我看来,预防胜于治疗。

这是我希望遵循的两个准则之一,以确保线程正常工作。另一个是确保每个线程完全负责自己的执行,因为它是唯一一个完全了解它在任何时间点正在做什么的。

这意味着没有Thread.stop调用,使用全局标志(或消息队列或类似的东西)来告诉您想要采取行动的另一个线程。然后让该线程完成实际工作。

答案 6 :(得分:4)

如果您使用的是Java 5,则可以调用ThreadMXBean上的方法findMonitorDeadlockedThreads(),您可以通过调用java.lang.management.ManagementFactory.getThreadMXBean()来调用该方法。这将只发现由对象监视器引起的死锁。在Java 6上有findDeadlockedThreads(),它还会发现由“可拥有的同步器”(例如ReentrandLockReentrantReadWriteLock)引起的死锁。

请注意,调用这些方法可能会很昂贵,因此它们只能用于故障排除。

答案 7 :(得分:3)

不完全是您提出的问题,但是当发生死锁时,您可以对进程ID执行“kill -3”,并将线程转储转储到stdout。此外,1.6 jvm还有一些工具可以用gui方式做同样的事情。

答案 8 :(得分:3)

如果从命令行运行并且怀疑自己已经死锁,请尝试在Windows中按ctrl + break(在unix中使用ctrl + \)来获取线程转储。 见http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html

答案 9 :(得分:3)

博士。 JavaSpecialists的Heinz Kabutz编写了一个有趣且信息丰富的newsletter issue on Java deadlocks,并在another newsletter issue中描述了一个名为ThreadMXBean的东西。在这些之间,你应该很好地了解问题和一些指导你做自己的仪器。

答案 10 :(得分:3)

Java可以检测死锁(虽然不是在运行时,它仍然可以诊断并报告死锁)。

例如,当使用稍微修改过的'Saurabh M. Chande'代码时(将其更改为Java并添加一些时间来保证每次运行时锁定)。一旦你运行它并且它就会死锁,如果你输入:

kill -3 PID   # where 'PID' is the Linux process ID

它将生成堆栈转储,其中包含以下信息:

Found one Java-level deadlock:
=============================
"Thread-0":
     waiting to lock monitor 0x08081670 (object 0x7f61ddb8, a Deadlock$A),
     which is held by "main"
"main":
      waiting to lock monitor 0x080809f0 (object 0x7f61f3b0, a Deadlock$B),
      which is held by "Thread-0"

答案 11 :(得分:2)

如果你在eclipse中调试,你可以暂停应用程序(在调试视图中选择应用程序和调试工具栏上的小||按钮)然后它可以报告死锁。

有关示例,请参阅http://runnerwhocodes.blogspot.com/2007/10/deadlock-detection-with-eclipse.html

答案 12 :(得分:1)

从Java 1.8开始,您可以通过在终端上使用jcmd命令轻松地找到程序是否有死锁。

  1. 在终端上运行jcmd:它列出了所有使用Java的程序(PID和名称)。
  2. 现在,运行jcmd <PID> Thread.print:这将在控制台上打印线程转储,并且在程序出现死锁时也将打印。

您可以使用命令jcmd列出的更多jcmd <PID> help选项来分析Java程序

答案 13 :(得分:0)

Java 5引入了ThreadMXBean - 一个为线程提供各种监视方法的接口。 ...区别在于findDeadlockedThreads还可以检测由所有者锁(java.util.concurrent)引起的死锁,而findMonitorDeadlockedThreads只能检测监视器锁(即同步块)

或者您可以以编程方式检测它,请参阅此https://dzone.com/articles/how-detect-java-deadlocks

答案 14 :(得分:-1)

经过这么长时间,我能够编写最简单的死锁示例。 欢迎评论。

Class A
{
  synchronized void methodA(B b)
  {
    b.last();
  }

  synchronized void last()
  {
    SOP(“ Inside A.last()”);
  }
}

Class B
{
  synchronized void methodB(A a)
  {
    a.last();
  }

  synchronized void last()
  {
    SOP(“ Inside B.last()”);
  }
}


Class Deadlock implements Runnable 
{
  A a = new A(); 
  B b = new B();

  // Constructor
  Deadlock()
  {
    Thread t = new Thread(); 
    t.start();
    a.methodA(b);
  }

  public void run()
  {
    b.methodB(a);
  }

  public static void main(String args[] )
  {
    new Deadlock();
  }
}

答案 15 :(得分:-4)

你必须在死锁类

中稍微修改一下代码
   Deadlock() {
    Therad t = new Thread(this); // modified
    t.start();
    System.out.println(); //any instruction to delay
    a.methodA(b);
}

此外,上述代码不会总是导致死锁,只会发生一些时间。