You may wonder if double checked locking (DCL) works in Java. It does and it doesn’t. I explain why.
What is Double Checked Locking?
The idea is that if you want to initialize something only on it’s very first access (lazy) you simply check if it already exists. If it exists you return it, if it doesn’t you create it.
But when two threads access such code in parallel it might fail to create only one instance. Both will see null
(the placeholder for no instance) and both will create a new instance.
Here’s an example:
private Foo foo = null; public Foo getFoo() { if (this.foo == null) { // first check synchronized(this) { // lock on this if (this.foo == null) // second check this.foo = new Foo(); } } assert null != this.foo; // it isn't null now return this.foo; }
I have explicitly added this
to each access of foo
(as I always do). This shows that the JVM often has to access the object. This takes some time.
Double Checked Locking never really works
When you implement DCL you can never be sure that an instance is fully constructed, when it is seen by another thread. If the reference is null then the thread will wait. But if it isn’t null then the thread will use it, even if another thread is still running the constructor of that object.
Volatile comes close to it
It can work if the field is volatile:
private volatile Foo foo = null;
Then each access to foo
is as if it was synchronized, or half synchronized as some call it. This means that when a reference is assigned to foo
, all threads will see the fully constructed object, and not an object that is still being constructed in another thread.
On primitives that use 64 bits (long and double) you also have to use volatile. if not the JVM could read the first half (32 bits) and then the second half of the value. If you use any other primitive type (int, short etc) you don’t really need volatile, but there’s a risk that two threads will calculate the value in parallel (see java.lang.String.hashCode()
as an example).
So with volatile you can have code that looks like DCL, but isn’t. However, it is actually a bit faster in some situations. Access to a volatile field is faster than full synchronization. In most cases you only need half a synchronization to get the value.
The JVM must not inline a constructor if the new reference is to be assigned to a volatile field. And if the JVM uses 64 bit values for references, it always has to write all 64 bits in one atomic operation. So the reference is valid when it is visible.
One thought on “Double Checked Locking in Java”