February 8, 2014

Simple Java Deadlock Example Explained


There are several good Java deadlock examples on the internet. I tried to write the simplest example which is readable and also executable. I used the deadlock example in the Java Tutorial, which I had to re-write, because it did not always produced a deadlock when executed with JUnit:



Explanation


For a deadlock we obviously need two objects, two threads and two synchronized methods in each object that call each other. (With one method per object it would be rather an endless loop. :-)

If we start the two threads with the objects locking each-other, most often - but not always - the following would happen:


So no deadlock would occur. The idea in the example is that after lock1 we should delay lock2, so that the other lock1 has a chance to lock the object before the first lock2 tries it. For the delaying we simply use a sleep before lock2 as shown on the next drawing:


In this case when Lockable1 tries to lock Lockable2, it will fail because that is already locked by another thread. But also Lockable2 will not proceed because of the same reason. (Actually the release events on the image will never occur.)

Example


You can execute the example on two ways:
  • as a Java application: it will never finish,
  • as a JUnit test case: a fail after 2 seconds indicates that there was a deadlock.

import org.junit.Test;

public class DeadlockTest {

    public static void main(final String[] args) throws InterruptedException {
        doDeadlock();
    }

    @Test(timeout = 2000)
    public void testDeadlock() throws InterruptedException {
        doDeadlock();
    }

    private static void doDeadlock() throws InterruptedException {
        final Lockable a = new Lockable();
        final Lockable b = new Lockable();
        final Thread lockAB = createLock(a, b);
        final Thread lockBA = createLock(b, a);
        lockAB.start();
        lockBA.start();
        lockAB.join();
        lockBA.join();
    }

    private static Thread createLock(final Lockable from, final Lockable to) {
        return new Thread() {
            @Override
            public void run() {
                try {
                    to.lock1(from);
                } catch (final InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
    }

    static class Lockable {
        public synchronized void lock1(final Lockable other) throws InterruptedException {
            Thread.sleep(1000);
            other.lock2();
        }

        public synchronized void lock2() {
            System.out.println("If you see this there is no deadlock.");
        }
    }
}

Outlook


Other articles about detecting and preventing deadlocks:

No comments :

Post a Comment