Java中的死锁检测

isr*_*kir 62 java multithreading deadlock

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

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

sta*_*fan 79

从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);
     }
  }
Run Code Online (Sandbox Code Playgroud)

  • java 有这个真是太棒了..!我想我快要崩溃并开始哭泣 (2认同)

Ste*_*e K 18

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


Ale*_*ler 11

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

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


Dav*_*ths 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!");
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


luk*_*uke 6

通常,java不提供死锁检测.与使用显式锁定的语言相比,synchronized关键字和内置监视器使得解决死锁更加困难.

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

  • java.util.concurrent.Lock中的锁具有从JDK 1.6开始内置的死锁检测.请参阅java.lang.management.ThreadMXBean. (3认同)

Jea*_*ach 6

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

例如,当使用下面的“Saurabh M. Chande”代码的稍微修改版本时(将其更改为 Java 并添加一些计时以保证每次运行的锁定)。一旦你运行它并且它死锁,如果你输入:

kill -3 PID   # where 'PID' is the Linux process ID
Run Code Online (Sandbox Code Playgroud)

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

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"
Run Code Online (Sandbox Code Playgroud)


pax*_*blo 5

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

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

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

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

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

  • 虽然这是规范的答案,但并不总是可能的。还有其他方法可以避免死锁。例如,您可以使用定时 tryLocks(带有 ReentrantLock)来进行回退。 (2认同)