Deadlock that you can’t avoid

A seemingly innocent user report in the Hudson IRC channel turns into an interesting “discovery” (for me anyway) about JVM. Namely, if you got two threads initializing classes in the opposite order, you can get into a dead lock.

For this test, I wrote the following class. In this way, initialization of Foo results in the initialization of Bar:

package test;
public class Foo {
    static {
        try {
            System.out.println("Initializing Foo");
            Thread.sleep(3000);
            new Bar();
            System.out.println("Foo initialized");
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

I then wrote the Bar class that does the opposite:

package test;
public class Bar {
    static {
        try {
            System.out.println("Initializing Bar");
            Thread.sleep(3000);
            new Foo();
            System.out.println("Bar initialized");
        } catch (Exception e) {
            throw new Error(e);
        }
    }
}

Now, if you initialize them simultaneously from the opposite direction like this:

public class App {
    public static void main(String[] args) {
        new Thread() { public void run() { new Foo(); }
        }.start();
        new Thread() { public void run() { new Bar(); }
        }.start();
    }
}

And you’ll see that it deadlocks:

"Thread-1" prio=10 tid=0x0000000040696000 nid=0x2d6e in Object.wait() [0x00007ff087ce5000]
   java.lang.Thread.State: RUNNABLE
	at test.Bar.(Bar.java:11)
	at test.App$2.run(App.java:14)

"Thread-0" prio=10 tid=0x0000000040688000 nid=0x2d6d in Object.wait() [0x00007ff087de6000]
   java.lang.Thread.State: RUNNABLE
	at test.Foo.(Foo.java:11)
	at test.App$1.run(App.java:8)

Obviously, in production code, the path from initialization of class Foo to class Bar will be much longer, but you get the idea. I’m kind of surprised that this isn’t a real widespread problem in JavaEE. Developers don’t normally care about the class initialization, and on the server side you tend to have a lot of threads doing random things…

22 thoughts on “Deadlock that you can’t avoid”

  1. Interesting bug. Do you find that classes are often doing more-than-minor amounts of work in static initializers? Perhaps it is not seen more often because this pattern is not used that much.

  2. In JavaEE, most classes are loaded when deploying on an app server. The deployment is single thread. Maybe that’s why we seldom see this.

  3. Isn’t it somethign that can be avoided by using an IOC container and injecting dependencies rather than letting object A initializes its depdendency B.

  4. Doesn’t this really highlight the need for good coding and design? When I look at two classes with circular dependencies, that speaks of poor design to me, not necessarily a fault of the JVM.

  5. Mao Geng: I used to work for the group that does JavaEE, and I can assure you that most classes are not loaded during the deployment. Classloading in general happens rather lazily in Java, for good reasons.

  6. Phil A: Yes. But even in an IoC environment, not every instantiation happens through the container and you can definitely have some static initializers. But it might explain why we see this not as frequently as I thought.

  7. Harsha: note that there’s no explicit synchronization involved in this code, and you can’t fix this in the same way you fix the code example you cited.

  8. Jan Kronquist: Thanks for the pointer! It’s good to confirm that this is indeed a problem. I also find it interesting that .NET avoids this problem.

  9. Chris A: bad coding and poor design can be blamed for everything from dangling pointers in C to slow database access in Java, so while it might be true that a “better” program can avoid this problem, I find that argument uninteresting — as you can see in the thread cited by Jan Kronquist, I think JVM could have done better in this regard, and in that sense I consider this a shortcoming of the platform.

  10. @kohsuke

    Is this an indication that you feel Java is losing its appeal for you as an Enterprise Platform, or simply that it shares imperfection with other development frameworks?

    If so, without getting into a bigger discussion and losing focus, the JCP should allow the community to fix this issue should it not? I read Jan Kronquists link, but I still feel a remedy is possible if carefully thought out.

    I noted the previous comment mentioning .NET’s avoidance of the threading issue.

  11. It remembers me some trouble i had when i wrote my first lines of code some years ago. I can’t really remember what i was exactly doing, i think it was something like:

    public class A {

    }

  12. @Vinicius

    public class A {
    B b = new B();
    }

    public class B {
    A a = new A();
    }

    It always crashed JVM when any of these classes were instantiated. But my memory can be wrong.. :)

  13. kohsuke :
    Mao Geng: I used to work for the group that does JavaEE, and I can assure you that most classes are not loaded during the deployment. Classloading in general happens rather lazily in Java, for good reasons.

    As long as user developed class is loaded through single thread at start, shouldn’t be a problem.

  14. lwpro2: That’s like saying “no dead lock will occur as long as you only ues one thread.” True, but it doesn’t help those of us who want to use multiple threads.

    Classloading happens automatically in Java, making it difficult to load everything from a single class.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>