Introduction
Yesterday I was talking to a good friend in the park and the topic of double checked locking for lazy instantiating of singletons came up. (Why we talked about this on a nice sunny day in the park is a separate issue, which won't be addressed in the blog ;)) At the time I was sure it was broken but I couldn't explain the reasons behind this strange behaviour. So I decided to have a look at it and write a quick blog about it.
How does double check locking look like in Java:
public class DoubleCheckedLocking { private static DoubleCheckedLocking singleton; private final String a; private final String b; private DoubleCheckedLocking() { this.a = "A"; this.b = "B"; } public DoubleCheckedLocking getSingleton() { if(singleton == null) { //1 synchronized(this) { //2 if(singleton == null) { //3 singleton = new DoubleCheckedLocking(); //4 } } } return singleton; //5 } }The idea behind this pattern is that you want to lazy initialise your singleton but you want to avoid the performance penalty which comes from declaring the whole method
synchronized
. In the pattern the first thread would enter the method in line 1 and check that the singleton is null
. It would then synchronize on the objects internal lock on line 2 and do a null
check on the value of the singleton again on line 3. If the null check holds true the singleton will be initialised on line 5 finally. All further calls to the getSingleton method wouldn't see the singleton variable as null and would just return the cached instance.
Unfortunately this pattern is not thread-safe and is considered broken. What could possibly go wrong in this situation? The problem sits on line 4. Assigning a reference to a variable and running the constructor is not an atomic operation. Two things need to happen:
- Run the constructor
- Assign newly created instance to variable (singleton)
What can you do?
The easiest way to fix this is to not use lazy instantiation at all or synchronize the whole method.
What if you must have the use lazy loading and can't synchronize the whole method? You can also declare the variable
public class ThreadSafeLoading { private static final ThreadSafeLoading singleton = new ThreadSafeLoading(); public ThreadSafeLoading getSingleton() { return singleton; } }This works very well. The code to initiate a static variable runs during classloading time and before the class can be instantiated. The variable is declared final so it is ensured that all clients calling
getSingleton
will refer to the same instance of the class.
What if you must have the use lazy loading and can't synchronize the whole method? You can also declare the variable
singleton
as volatile. That way the JVM will bring memory barriers in place which will stop the possibility for out-of-order processing. Each write to volatile variable and each read from a volatile variable will create memory barrier and ensure that two threads will see actions in the other thread in proper order.public class DoubleCheckedLocking {
private static volatile DoubleCheckedLocking singleton;
private final String a;
private final String b;
private DoubleCheckedLocking() {
this.a = "A";
this.b = "B";
}
public DoubleCheckedLocking getSingleton() {
if(singleton == null) { //1
synchronized(this) { //2
if(singleton == null) { //3
singleton = new DoubleCheckedLocking(); //4
}
}
}
return singleton; //5
}
}