I have come across a situation where my program hangs, looks like deadlock. But I tried figuring it out with jconsole and visualvm, but they didn\'t detect any deadlock. Sam
I was able to get your program to run by commenting out the line state = 11;
You can not set state=11 until you have completed initialization. You can not complete initialization until t1 finishes running. T1 can not finish running until you set state=11. Deadlock.
You're not just starting another thread - you're joining on it. That new thread has to wait for StaticInitializer
to be fully initialized before it can proceed, because it's trying to set the state
field... and initialization is already in progress, so it waits. However, it's going to be waiting forever, because that initialization is waiting for that new thread to terminate. Classic deadlock.
See the Java Language Specification section 12.4.2 for details about what's involved in class initialization. Importantly, the initializing thread will "own" the monitor for StaticInitializer.class
, but the new thread will be waiting to acquire that monitor.
In other words, your code is a bit like this non-initializer code (exception handling elided).
final Object foo = new Object();
synchronized (foo)
{
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (foo) {
System.out.println("In the new thread!");
}
});
t1.start();
t1.join();
});
If you can understand why that code would deadlock, it's basically the same for your code.
The moral is not to do much work in static initializers.
Here is what I think happens:
StaticInitializer
. This involves locking the corresponding Class
object.run()
method attempts to access state
, which requires StaticInitializer
to be fully initialized; this involves waiting on the same lock as in step 1.End result: a deadlock.
See the JLS for a detailed description of the intialization procedure.
If you move t1.join()
into main()
, everything works.
classloading is kind of a sensitive time in the jvm. when classes are being initialized, they hold an internal jvm lock which will pause any other thread trying to work with the same class. so, your spawned thread is most likely waiting for the StaticInitializer class to be fully initialized before proceeding. however, your StaticInitializer class is waiting for the thread to complete before being fully initialized. thus, deadlock.
of course, if you are truly trying to do something like this, the secondary thread is superfluous since you are joining it right after you start it (so you might as well just execute that code directly).
UPDATE:
my guess as to why the deadlock is not detected is because it is occurring at a much lower level than the level at which the standard deadlock detection code works. that code works with normal object locking, whereas this is deep jvm internal stuff.