很久以前,我从Java参考书中保存了一句话:"Java没有处理死锁的机制.它甚至不知道发生了死锁." (Head First Java 2nd Edition,p.516)
那么,它是什么呢?有没有办法在Java中捕获死锁案例?我的意思是,有没有一种方法可以让我们的代码了解发生死锁的情况?
从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); } }
JConsole能够检测正在运行的应用程序中的死锁.
JDK 5和6将把持有的锁信息转储到完整的线程转储中(使用kill -3,jstack,jconsole等获得).JDK 6甚至包含有关ReentrantLock和ReentrantReadWriteLock的信息.从这些信息中可以通过找到锁定周期来诊断死锁:线程A保持锁定1,线程B保持锁定2,并且A正在请求2或B正在请求1.根据我的经验,这通常是非常明显的.
其他分析工具实际上可以找到潜在的死锁,即使它们没有发生.来自OptimizeIt,JProbe,Coverity等供应商的线程工具是很好看的地方.
请注意,使用并发包非常难以调试的死锁类型.这就是你有一个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!"); } } } }
通常,java不提供死锁检测.与使用显式锁定的语言相比,synchronized关键字和内置监视器使得解决死锁更加困难.
我建议迁移到使用java.util.concurrent.Lock锁等,以使您的锁定方案更容易推理.实际上,您可以轻松地使用死锁检测自行实现锁定接口.该算法基本上遍历锁依赖图并寻找一个循环.