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.<clinit>(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.<clinit>(Foo.java:11)
at test.App$1.run(App.java:8)
</clinit></clinit>
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…