JDK 1.5以来,java.lang.management
包中提供了非常有用的方法来查找和检查发生的死锁。请参阅ThreadMXBean
类的findMonitorDeadlockedThreads()
和findDeadlockedThreads()
方法。
使用这种方法的一种可能方式是拥有一个单独的看门狗线程(或定期任务)来执行此操作。
示例代码:
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);
}
}
JDK 5和6将在完整的线程转储(使用kill -3、jstack、jconsole等获得)中转储持有锁的信息。 JDK 6甚至包含有关ReentrantLock和ReentrantReadWriteLock的信息。通过这些信息,可以诊断死锁并找到锁定循环:线程A持有锁1,线程B持有锁2,要么A请求2,要么B请求1。根据我的经验,这通常是非常明显的。
其他分析工具实际上可以找到潜在的死锁,即使它们没有发生。来自OptimizeIt、JProbe、Coverity等供应商的线程工具是寻找问题的好地方。
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锁等,以便更容易地理解您的锁定方案。实际上,您可以轻松地实现具有死锁检测的锁接口。算法基本上是遍历锁依赖图并查找循环。
Thread.stop
调用,使用全局标志(或消息队列或类似的东西)告诉另一个线程您想要采取行动。然后让那个线程执行实际工作。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"
java.lang.management.ManagementFactory.getThreadMXBean()
获取ThreadMXBean,并在其上调用findMonitorDeadlockedThreads()
方法。这将仅查找由对象监视器引起的死锁。在Java 6中,有findDeadlockedThreads()
方法,它还会查找由“可拥有同步器”(例如ReentrantLock
和ReentrantReadWriteLock
)引起的死锁。
请注意,调用这些方法可能会很昂贵,因此应仅用于故障排除目的。
虽然不完全符合您的要求,但当死锁发生时,您可以对进程ID执行“kill -3”,它会将线程转储到标准输出。此外,1.6 JVM还有一些工具以图形界面的方式完成相同的操作。
如果您正在命令行中运行,并且怀疑出现了死锁,请尝试在Windows中使用ctrl+break(在Unix中为ctrl+\)以获取线程转储。 参见http://java.sun.com/javase/6/webnotes/trouble/TSG-VM/html/gbmps.html