Search code examples
javamultithreadingconcurrencylockingdeadlock

Why does this contrived Java code deadlock?


I'm having a hard time understanding synchronized. Since the first thread doesn't do anything with the object 2, doesn't it "unlock" everything in a second?

public class Uninterruptible {
    public static void main(String[] args) throws InterruptedException {
        final Object o1 = new Object(); final Object o2 = new Object();

        Thread t1 = new Thread() {
            public void run() {
                try {
                    synchronized(o1) {
                        Thread.sleep(1000);
                        synchronized(o2) {}
                    }
                } catch(InterruptedException e) { System.out.println("t1 interrupted"); }
            }
        };

        Thread t2 = new Thread() {
            public void run() {
                try {
                    synchronized(o2) {
                        Thread.sleep(1000);
                        synchronized(o1) {}
                    }
                } catch(InterruptedException e) { System.out.println("t2 interrupted"); }
            }
        };

        t1.start(); t2.start();
        Thread.sleep(2000);
        t1.interrupt(); t2.interrupt();
        t1.join(); t2.join();

        System.out.println("Donezo!");

    }
}

Solution

  • It doesn't matter that the inner synchronized blocks do nothing. Java will still attempt to acquire the lock on the the object specified.

    No matter whether you have nothing or a huge amount of processing in the inner synchronized blocks, what you have is the minimal example to create a deadlock: two different threads, each owning the lock on a distinct resource, each attempting to acquire the lock on each other's resource.

    The deadlock occurs before either thread even gets to executing the inner synchronized block, because neither thread can get locks on both resources at the same time.

    The code winds up doing nothing but hanging, with each thread blocked. Your calls to interrupt are too late to cause an InterruptedException; they only set the "interrupted status" in the Thread. Commenting out Thread.sleep(2000) will let the calls to interrupt catch the Threads while they are still sleeping, before they even attempt to acquire the second lock.