Saturday 9 April 2011

Simple Java deadlock detector

Thanks to extensive testing, Application MultithreadedMessenger from QualitySoftware Inc., has been running without any critical bugs since it's go-live last month. The customer, MrBigBank is happy faced since it is one of the important pieces of his solution architecture. One fine early morning MultithreadedMessenger stopped working. After an hour, a smart guy at the back office realised the application is busy fighting within itself and not ready to serve the incoming requests. Knowing that it is not going to return ever, he restarts the application and phones the vendor's help desk for a solution.

Though deadlocks related bugs are rare in a production quality system, when they appear, sometime there would be hours of complex investigation required. Java provides enough help in identifying threads which are deadlocked through it's management library. The following is a simple example where we create a dead lock situation and a deadlock detector to detect and report when the deadlock occurs.

Class Messenger is a mock application which provides send and receive methods to handle the messaging. This class is written (well, badly!) just to create a situation where two threads will get into waiting for each other.

Messenger.java

public class Messenger {

 private Lock senderLock = new Lock();
 private Lock receiverLock = new Lock();

 public void send(String message) {
  synchronized (senderLock) {
   synchronized (receiverLock) {
    //some code here
   }
  }
 }

 public String receive() {
  synchronized (receiverLock) {
   synchronized (senderLock) {
    // some code here
    return "SUCCESS";
   }
  }
 }
}

class Lock {
}

A DeadlockDetector uses the managed beans provided by java.lang.management package to detect the threads which are deadlocked and reports with the thread information.

DeadlockDetector.java

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector implements Runnable {

 private volatile boolean _detected = false;

 public synchronized boolean detected() {
  return _detected;
 }

 @Override
 public void run() {
  while (true) {
   ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
   long[] deadlockedThreads = tmx.findDeadlockedThreads();
   if (deadlockedThreads != null) {
    ThreadInfo[] threadInfos = tmx.getThreadInfo(deadlockedThreads);
    for (ThreadInfo threadInfo : threadInfos) {
     System.out.println(threadInfo);
    }
    _detected = true;
    break;
   }
  }
 }
}

Finally, DeadlockTest class which runs the Messenger application and the DeadlockDetector to halt and report when the deadlock occurs.

DeadlockTest.java

public class DeadlockTest {

 public static void main(String[] args) {
  
  DeadlockDetector deadlockDetector = new DeadlockDetector();
  new Thread(deadlockDetector).start();

  final Messenger messenger = new Messenger();

  while (!deadlockDetector.detected()) {

   new Thread(new Runnable() {

    @Override
    public void run() {
     String message = "blah blah";
     messenger.send(message);
    }
   }, "foo").start();

   new Thread(new Runnable() {

    @Override
    public void run() {
     String message = messenger.receive();
    }
   }, "bar").start();
  }
 }
}

Much like the same way, other managed beans for memory, gc,class loading, compilation and runtime are accessible with simple test classes.

The same managed beans feed information to utilities like jconsole or jvisualvm, making it possible to completely monitor and manage the JVM.

No comments:

Post a Comment