Deadlock detection in Java
Since JDK 1.5 there are very useful methods in the java.lang.management
package to find and inspect deadlocks that occurs. See the findMonitorDeadlockedThreads()
and findDeadlockedThreads()
method of the ThreadMXBean
class.
A possible way to use this is to have a separate watchdog thread (or periodic task) that does this.
Sample code:
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 is able to detect deadlocks in a running application.
JDK 5 and 6 will dump held lock information in a full thread dump (obtained with kill -3, jstack, jconsole, etc). JDK 6 even contains information about ReentrantLock and ReentrantReadWriteLock. It is possible from this information to diagnose a deadlock by finding a lock cycle: Thread A holds lock 1, Thread B holds lock 2, and either A is requesting 2 or B is requesting 1. From my experience, this is usually pretty obvious.
Other analysis tools can actually find potential deadlocks even if they don't occur. Thread tools from vendors like OptimizeIt, JProbe, Coverity, etc are good places to look.
Note that there is a type of deadlock using the concurrent package that is very hard to debug. That is where you have a ReentrantReadWriteLock and one thread grabs the read lock and then (say) tries to enter a monitor held by some other thread that is also waiting to grab the write lock. What makes it especially hard to debug is that there is no record of who has entered a read lock. It is simply a count. The thread might even have thrown an exception and died leaving the read count non-zero.
Here is a sample deadlock that the findDeadlockedThreads method mentioned earlier won't get:
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!");
}
}
}
}
In general java does not offer deadlock detection. The synchronized keyword and built in monitors make it somewhat more difficult to reason about deadlock than in languages with explicit locking.
I would suggest migrating to using java.util.concurrent.Lock locks and the like in order to make your locking schemes easier to reason about. In fact you could easily make your own implementation of the lock interface with deadlock detection. The algorithm is to basically traverse the lock dependency graph and look for a cycle.