/**
*
* This is a simple Manager class which will generate a thread deadlock in Java,
* using "synchronized" blocks on a pair of objects. The process is:
*
* The process is:
*
* 1. Create two threads, each designed to lock a pair of objects.
* 2. Thread1 locks Obj1 and then sleeps a bit.
* 3. Thread2 locks Obj2 and then sleeps a bit.
* 4. Each thread wakes, and Thread1 tries to lock Obj2 while Thread2 tries to lock Obj1.
* Since each object is locked due to the other thread being in a block synchronized
* on it, a deadlock occurs.
*
* In steps 2 & 3, we can either do a Thread.yield(), which normally suffices to cause
* the other thread to run, causing a deadlock, or we can do a sleep(10), which
* will nearly guarantee it.
*
* @author James Long (james AT jameslong DOT org)
* @version 1.0 10/12/2006
*/
class ResourceManager {
/**
* Ways to yield control from the current thread.
* YIELD - do a simple Yield
* SLEEP - put the thread to sleep for a period of time
*/
enum YieldType { YIELD, SLEEP };
/**
* Locks one object, yields control, and then locks the other object.
*
* @param requestor The requesting object (used for display purposes only).
* @param o1 The first object to lock.
* @param o2 The second object to lock.
* @param yt The type of yield to perform after the first lock.
* @return Returns void, but only if YIELD is specified, and the scheduler reschedules this thread before the other.
* @see ResourceManager.YieldType
*/
public static void lockTwoObjects(DeadlockGenerator requestor, Object o1, Object o2, YieldType yt) {
// Lock object o1 for the duration of this block.
synchronized(o1) {
System.out.println( requestor.hashCode() + " has locked " + o1.hashCode());
// Yield process control from this thread. This takes a lot of the "luck" out
// of causing the deadlock; otherwise you would have to hope there is a context
// switch between your two locks in Thread 1, wherein Thread 2 can lock its first.
// With the yield/sleep, it's almost guaranteed to happen.
if ( yt == YieldType.YIELD) {
Thread.yield();
} else {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
System.err.println("Thread was interrupted.");
};
}
System.out.println( requestor.hashCode() + " wants to lock " + o2.hashCode());
// Lock the second object.
synchronized(o2) {
// You'd probably do something useful here, in a real program.
}
System.out.println("Failed to cause deadlock. Try 'java DeadlockGenerator SLEEP' to try to force it");
}
}
}
/**
* Implements a thread-based class which will try to use the ResourceManager to lock a pair of
* objects. As the main class, it also creates the DeadlockGenerator Threads, and sets them
* running.
*
* @author James Long (james AT jameslong DOT org)
* @version 1.0 10/12/2006
*/
public class DeadlockGenerator extends Thread {
/**
* One of the objects to lock.
*/
private Object o1;
/**
* One of the objects to lock.
*/
private Object o2;
/**
* Which style of yield to use.
* @see ResourceManager.YieldType
*/
private ResourceManager.YieldType m_yield;
/**
* Main constructor - tells this objects to lock object a, then object b, and stop
* by YieldType yt
*
* @see ResourceManager#YieldType
*/
public DeadlockGenerator( Object a, Object b, ResourceManager.YieldType yt ) {
o1 = a;
o2 = b;
m_yield = yt;
}
/**
* Main entry point to the program. Creates the DeadlockGenerator threads and
* starts them running.
* @return Never returns, if all goes well.
*/
public static void main (String[] args) {
Object o1 = new Object();
Object o2 = new Object();
DeadlockGenerator t1 = null;
DeadlockGenerator t2 = null;
// YieldType.YIELD tells the threads to do a Thread.yield() after
// it grabs the first lock. This is usually enough to ensure the second
// thread gets a lock before the first thread is reactivated. However,
// Yield in no way guarantees which thread runs next. Thus, if you
// really want to force the deadlock, switch to SLEEP instead, which
// does a 10 second sleep, which should be sufficient.
if (args.length > 0 && args[0].equals("SLEEP")) {
System.out.println("Using 'SLEEP' method.");
t1 = new DeadlockGenerator(o1, o2, ResourceManager.YieldType.SLEEP);
t2 = new DeadlockGenerator(o2, o1, ResourceManager.YieldType.SLEEP);
} else {
System.out.println("Using 'YIELD' method.");
t1 = new DeadlockGenerator(o1, o2, ResourceManager.YieldType.YIELD);
t2 = new DeadlockGenerator(o2, o1, ResourceManager.YieldType.YIELD);
}
t2.start();
t1.start();
}
/**
* This function is called when the thread is started.
*
* @see java.lang.Thread
*/
public void run() {
ResourceManager.lockTwoObjects(this, o1, o2, m_yield);
}
}