[ Team LiB ] Previous Section Next Section

4.4 Deadlock

Multithreaded programming requires a programmer to take special care in several areas. For example, if multiple threads can be changing the state of an object at the same time, you typically must use synchronized methods or the synchronized statement to ensure that only one thread changes the object's state at a time. If you do not, two threads could end up overwriting each other's edits, leaving the object in an inconsistent state.

Unfortunately, using synchronization can itself cause problems. Thread synchronization involves acquiring an exclusive lock. Only the one thread that currently holds the lock can execute the synchronized code. When a program uses more than one lock, however, a situation known as deadlock can arise. Deadlock occurs when two or more threads are all waiting to acquire a lock that is currently held by one of the other waiting threads. Because each thread is waiting to acquire a lock, none ever releases the lock or locks it already holds, which means that none of the waiting threads ever acquires the lock it is waiting for. The situation is a total impasse; all the threads involved come to a halt, and the program can't continue.

Example 4-4 is a simple program that creates a deadlock situation in which two threads attempt to acquire locks on two different resources. It is pretty easy to see how deadlock can arise in this simple program. It might not be as clear, however, if there were synchronized methods involved, instead of a simple symmetrical set of synchronized statements. More complicated situations also arise with multiple threads and multiple resources. In general, the problem of deadlock is a deep and nasty one. One good technique for preventing it, however, is for all threads always to acquire all the locks they need in the same order.

Example 4-4. Deadlock.java
package je3.thread;

/**
 * This is a demonstration of how NOT to write multi-threaded programs.
 * It is a program that purposely causes deadlock between two threads that
 * are both trying to acquire locks for the same two resources.
 * To avoid this sort of deadlock when locking multiple resources, all threads
 * should always acquire their locks in the same order.
 **/
public class Deadlock {
    public static void main(String[  ] args) {
        // These are the two resource objects we'll try to get locks for
        final Object resource1 = "resource1";
        final Object resource2 = "resource2";
        // Here's the first thread.  It tries to lock resource1 then resource2
        Thread t1 = new Thread( ) {
                public void run( ) {
                    // Lock resource 1
                    synchronized(resource1) {
                        System.out.println("Thread 1: locked resource 1");
                        
                        // Pause for a bit, simulating some file I/O or
                        // something.  Basically, we just want to give the
                        // other thread a chance to run.  Threads and deadlock
                        // are asynchronous things, but we're trying to force
                        // deadlock to happen here...
                        try { Thread.sleep(50); }
                        catch (InterruptedException e) {  }
                        
                        // Now wait 'till we can get a lock on resource 2
                        synchronized(resource2) {
                            System.out.println("Thread 1: locked resource 2");
                        }
                    }
                }
            };
        
        // Here's the second thread.  It tries to lock resource2 then resource1
        Thread t2 = new Thread( ) {
                public void run( ) {
                    // This thread locks resource 2 right away
                    synchronized(resource2) {
                        System.out.println("Thread 2: locked resource 2");
                        
                        // Then it pauses, just like the first thread.
                        try { Thread.sleep(50); }
                        catch (InterruptedException e) {  }
                        
                        // Then it tries to lock resource1.  But wait!  Thread
                        // 1 locked resource1, and won't release it 'till it
                        // gets a lock on resource2.  This thread holds the
                        // lock on resource2, and won't release it 'till it
                        // gets resource1.  We're at an impasse. Neither
                        // thread can run, and the program freezes up.
                        synchronized(resource1) {
                            System.out.println("Thread 2: locked resource 1");
                        }
                    }
                }
            };
        
        // Start the two threads. If all goes as planned, deadlock will occur, 
        // and the program will never exit.
        t1.start( ); 
        t2.start( );
    }
}
    [ Team LiB ] Previous Section Next Section