Thursday, October 27, 2011

Create and Detect Thread Deadlocks

Just the code:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.util.Date;
import java.util.Scanner;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* Example of how a deadlock might be created, and how to detect it. Two threads
* are launched, each acquiring the same two resources but in a different order.
* A delay is introduced in the first thread before acquiring the second
* resource to set up a race condition such that a deadlock *may* be introduced.
* The deadlock is not guaranteed to occur; it depends on the length of the
* delay, which is controlled by the user executing the program. Empirically, on
* my desktop, I've found that values of 100 sometimes are adequate, while
* values of 1000+ become increasingly reliable.
*
* Note that looking for deadlocks with the JMX mechanism used here (ThreadMXBean) is
* described in the javadocs as an operation that "might be expensive". As such,
* unless you're confident that the cost is not high...I'd recommend not putting
* this into operation, rather using it in dev/test environment when trying to
* reproduce a situation observed in production where a deadlock occurred. Note
* that in production, issuing SIGQUIT (ctl-backslash) will provoke a thread
* dump (without killing the process), in which information on deadlocked
* threads will also appear.
*/
public class DeadlockedThreadFinder
{
// done becomes true when either thread completes; note that if one thread
// completes, then the other one will also
public boolean done;
/**
* Launch two threads, each acquiring a lock on two different resources in a
* different order. The first thread pauses some time T as a function of the
* given delay.
*
* @param delay is the given delay
*/
public void createDeadlock(final int delay)
{
System.out.println("First thread will pause for time T as function of " +
delay);
// the resources in contention - if threads use the synchronized keyword,
// you could use something like the two Object declared (and commented
// out) here; else, use the ReentrantLock if in java.util.concurrent
// land.
// final Object resource1 = new Object();
// final Object resource2 = new Object();
final Lock lock1 = new ReentrantLock();
final Lock lock2 = new ReentrantLock();
// first thread
Thread t1 = new Thread()
{
public void run()
{
long id = this.getId();
lock1.lock(); // alternately use synchronized (resource1) without
// the try-finally block
try
{
System.out.println(id + ": obtained first lock");
// instead of using Thread.sleep(), just create a whole bunch
// of expensive objects
for (int i = 0; i < delay; i++)
{
new Date();
}
System.out.println(id + " waiting for 2nd lock...");
lock2.lock(); // or use synchronized (resource2)
try
{
System.out.println(id + ": obtained 2nd lock");
}
finally
{
lock2.unlock();
}
System.out.println(id + " is done!");
done = true;
}
finally
{
lock1.unlock();
}
}
};
// second thread
Thread t2 = new Thread()
{
public void run()
{
long id = this.getId();
lock2.lock(); // alternately use synchronized (resource2) without
// the try-finally block
try
{
System.out.println(id + ": obtained 2nd lock");
System.out.println(id + " waiting for first lock...");
lock1.lock(); // or use synchronized (resource1)
try
{
System.out.println(id + ": obtained first lock");
}
finally
{
lock1.unlock();
}
System.out.println(id + " is done!");
done = true;
}
finally
{
lock2.unlock();
}
}
};
t1.start();
t2.start();
}
/**
* User must enter an integer that specifies the delay. Values of 10000+ are
* likely to cause a deadlock. The larger the value entered, the more likely
* the program will loop a few times detecting no deadlock, and then, once
* the first thread has satisfied the delay, the program will detect a
* deadlock until the end of time or user termination of program, whichever
* comes first.
*/
public static void main(String[] args) throws Exception
{
// the JMX deadlock finder
ThreadMXBean mxbean = ManagementFactory.getThreadMXBean();
// user specifies the delay
System.out.print("Enter delay: ");
Scanner sc = new Scanner(System.in);
// launch the threads
DeadlockedThreadFinder finder = new DeadlockedThreadFinder();
finder.createDeadlock(sc.nextInt());
System.out.println("Waiting for either thread to be done...");
do
{
// the JMX deadlock finder will return null if no deadlock detected.
// Note that if the threads are using traditional synchronization (i.e.
// using the synchronized keyword), then you could also use the
// findMonitorDeadlockedThreads method here; but since the threads
// might uplevel to use java.util.concurrent, it's advised to instead
// always use findDeadlockedThreads, which will work for either situation.
long[] deadlocked = mxbean.findDeadlockedThreads();
if (deadlocked != null)
{
System.out.print("Deadlocked threads: ");
String comma = "";
for (long l : deadlocked)
{
System.out.print(comma + l);
comma = ",";
}
System.out.println();
}
else
{
System.out.println("No deadlock detected");
}
Thread.sleep(1000);
}
while (!finder.done);
// uh, done
System.out.println("Done");
}
}

No comments:

Post a Comment

Welcome to the Perimeter Sweep Blog

My blog is largely intended to be a placeholder for topics involving software development - architecture, technology drill-downs, best practices, various solutions, workarounds, gotchas and the like - things that will remind me what I've learned over time. If it helps you out also - all the better.

Subscribe To This Blog

About Me

I'm a Senior Software Engineer, an avid runner, and formerly a professional musician...currently the proud father of a super-tyke, raising two Siberian Huskies and married to my best friend. Life is good.