Class Loading Deadlocks
- by tomas.nilsson
Mattis follows up on his previous post with one more expose on Class Loading Deadlocks
As I wrote in a previous post, the class loading mechanism in Java is very powerful. There are many advanced techniques you can use, and when used wrongly you can get into all sorts of trouble. But one of the sneakiest deadlocks you can run into when it comes to class loading doesn't require any home made class loaders or anything. All you need is classes depending on each other, and some bad luck.
First of all, here are some basic facts about class loading:
1) If a thread needs to use a class that is not yet loaded, it will try to load that class
2) If another thread is already loading the class, the first thread will wait for the other thread to finish the loading
3) During the loading of a class, one thing that happens is that the <clinit method of a class is being run
4) The <clinit method initializes all static fields, and runs any static blocks in the class.
Take the following class for example:
class Foo {
static Bar bar = new Bar();
static {
System.out.println("Loading Foo");
}
}
The first time a thread needs to use the Foo class, the class will be initialized. The <clinit method will run, creating a new Bar object and printing "Loading Foo"
But what happens if the Bar object has never been used before either? Well, then we will need to load that class as well, calling the Bar <clinit method as we go.
Can you start to see the potential problem here? A hint is in fact #2 above.
What if another thread is currently loading class Bar? The thread loading class Foo will have to wait for that thread to finish loading. But what happens if the <clinit method of class Bar tries to initialize a Foo object? That thread will have to wait for the first thread, and there we have the deadlock. Thread one is waiting for thread two to initialize class Bar, thread two is waiting for thread one to initialize class Foo.
All that is needed for a class loading deadlock is static cross dependencies between two classes (and a multi threaded environment):
class Foo {
static Bar b = new Bar();
}
class Bar {
static Foo f = new Foo();
}
If two threads cause these classes to be loaded at exactly the same time, we will have a deadlock.
So, how do you avoid this? Well, one way is of course to not have these circular (static) dependencies. On the other hand, it can be very hard to detect these, and sometimes your design may depend on it. What you can do in that case is to make sure that the classes are first loaded single threadedly, for example during an initialization phase of your application.
The following program shows this kind of deadlock. To help bad luck on the way, I added a one second sleep in the static block of the classes to trigger the unlucky timing. Notice that if you uncomment the "//Foo f = new Foo();" line in the main method, the class will be loaded single threadedly, and the program will terminate as it should.
public class ClassLoadingDeadlock {
// Start two threads. The first will instansiate a Foo object,
// the second one will instansiate a Bar object.
public static void main(String[] arg) {
// Uncomment next line to stop the deadlock
// Foo f = new Foo();
new Thread(new FooUser()).start();
new Thread(new BarUser()).start();
}
}
class FooUser implements Runnable {
public void run() {
System.out.println("FooUser causing class Foo to be loaded");
Foo f = new Foo();
System.out.println("FooUser done");
}
}
class BarUser implements Runnable {
public void run() {
System.out.println("BarUser causing class Bar to be loaded");
Bar b = new Bar();
System.out.println("BarUser done");
}
}
class Foo {
static {
// We are deadlock prone even without this sleep...
// The sleep just makes us more deterministic
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
static Bar b = new Bar();
}
class Bar {
static {
try {
Thread.sleep(1000);
} catch(InterruptedException e) {}
}
static Foo f = new Foo();
}