Home > Uncategorized > Deadlock that you can’t avoid

Deadlock that you can’t avoid

September 1st, 2010

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…

Uncategorized

  1. Matt B
    September 1st, 2010 at 18:08 | #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. September 1st, 2010 at 18:39 | #2

    Yeah, I think sometimes they do. Like the singleton pattern.

  3. Mao Geng
    September 1st, 2010 at 22:47 | #3

    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.

  4. September 1st, 2010 at 22:48 | #4

    Seems like you are at risk for this if you ever have a dependency cycle between classes. Probably something to avoid in general.

  5. Phil A
    September 1st, 2010 at 23:46 | #5

    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.

  6. Harsha
    September 1st, 2010 at 23:51 | #6

    This is a classic case of lock ordering deadlock discussed in “Java concurrency in practice” Eg bad code http://www.javaconcurrencyinpractice.com/listings/CooperatingDeadlock.java
    Strategy suggested is to use open calls http://www.javaconcurrencyinpractice.com/listings/CooperatingNoDeadlock.java

  7. September 2nd, 2010 at 03:25 | #7

    Interesting! Apparently a known problem that was decided not to be fixed, see http://www.cs.umd.edu/~pugh/java/memoryModel/archive/1226.html

  8. Chris A
    September 2nd, 2010 at 06:02 | #8

    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.

  9. September 2nd, 2010 at 07:27 | #9

    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.

  10. September 2nd, 2010 at 07:30 | #10

    Zac Thompson: in a way yes, but in a way no. For example, if you don’t have any static initializers, you won’t get into this problem.

  11. September 2nd, 2010 at 07:35 | #11

    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.

  12. September 2nd, 2010 at 07:36 | #12

    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.

  13. September 2nd, 2010 at 07:40 | #13

    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.

  14. September 2nd, 2010 at 07:44 | #14

    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.

  15. September 6th, 2010 at 07:14 | #15

    @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.

  16. Vinicius
    September 8th, 2010 at 09:54 | #16

    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 {

    }

  17. Vinicius
    September 8th, 2010 at 09:57 | #17

    @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.. :)

  18. September 8th, 2010 at 13:16 | #18

    @Kazuki Hamano

    No, this post is just to share what I discovered. I think this kind of minor problems exist in all the platforms.

  19. September 8th, 2010 at 13:16 | #19

    @Vinicius

    You clearly got an infinite allocation loop.

  20. lwpro2
    September 16th, 2010 at 20:35 | #20

    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.

  21. January 13th, 2011 at 13:13 | #21

    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.

  1. January 28th, 2011 at 21:33 | #1