tempus-fugit

Java micro-library for writing & testing concurrent code

Detecting Deadlocks

The DeadlockDetector class allows you to programmatically detect basic deadlocks in your Java code. You can output deadlocks using the following code (note that printing a thread dump using the ThreadDump class will automatically attempt to find any deadlocks).

1
DeadlockDetector.printDeadlocks(System.out);

The DeadlockDecector class will spot Java monitor cyclic locking problems as well as Lock based cyclic problems. It’s implementation is basically the same as that used by jconsole and jstack. The types of deadlock it can detect can be illustrated in the example below.

Monitor Deadlock Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
public void potentialDeadlock() {
  new Kidnapper().start();
  new Negotiator().start();
}

public class Kidnapper extends Thread {
  public void run() {
     synchronized (nibbles) {
        synchronized (cash) {
            take(cash);
        }
     }
  }
}

public class Negotiator extends Thread {
  public void run() {
     synchronized (cash) {
        synchronized (nibbles) {
            take(nibbles);
        }
     }
  }
}

Here, the Kidnapper is unwilling to release poor Nibbles the Cat until he has the Cash but our Negotiator is unwilling to part with the Cash until he has poor Nibbles. The deadlock detector displays this situation as follows.

Deadlock detected
=================

"Negotiator-Thread-1":
  waiting to lock Monitor of ...DeadlockDetectorTest$Cat@ce4a8a
  which is held by "Kidnapper-Thread-0"

"Kidnapper-Thread-0":
  waiting to lock Monitor of ...DeadlockDetectorTest$Cash@7fc8b2
  which is held by "Negotiator-Thread-1"

See this example in full

Lock Based Deadlock Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private final Cash cash = ... // Cash extends ReentrantLock
private final Cat = ... // Cat extends ReentrantLock

@Test
public void potentialDeadlock() throws InterruptedException {
    new Kidnapper().start();
    new Negotiator().start();
}

public class Kidnapper extends Thread {
    public void run() {
        try {
            keep(nibbles);
            take(cash);
        } finally {
            release(nibbles);
        }
    }
}

public class Negotiator extends Thread {
    public void run() {
        try {
            keep(cash);
            take(nibbles);
        } finally {
            release(cash);
        }
    }
}

Where keep, take and release methods are pedagogically named methods wrapping the Lock.lock and Lock.unlock methods.

1
2
3
4
5
6
7
8
9
10
11
private void keep(Lock lock) {
    lock.lock();
}

private void take(Lock lock) {
    lock.lock();
}

private void release(Lock lock) {
    lock.unlock();
}

Same scenario as before, a deadlock ensues which is shown as.

Deadlock detected
=================

"Negotiator-Thread-3":
  waiting to lock Monitor of java.util.concurrent.locks.ReentrantLock$NonfairSync@266bade9
  which is held by "Kidnapper-Thread-2"

"Kidnapper-Thread-2":
  waiting to lock Monitor of java.util.concurrent.locks.ReentrantLock$NonfairSync@6766afb3
  which is held by "Negotiator-Thread-3"

See this example in full

Over to you...