Tuesday, January 30, 2007

Double-checked locking doesn't work in Java

This doesn't work in Java:

class Foo {
private Helper helper = null;

public Helper getHelper() {
if (helper == null)
synchronized(this) {
if (helper == null)
helper = new Helper();
}
return helper;
}
}
It is possible that to due to compiler-based reorderings helper reference will be initialised while the fields in Helper object will be at their default values. There are other more subtle problems with the double-checked locking approach. You can read the complete paper here.

In most cases such an optimisation is not worth the trouble. If you want to lazy-initialise a singleton, define it as a static field in another class:

class Helper {
class HelperSingleton {
static Helper singleton = new Helper();
}

public Helper getHelper() {
return HelperSingleton.singleton;
}
}
The semantics of Java guarantees that singleton field is going to be initialised only when referenced, and that all threads accessing the field will see the writes performed during the initialisation.

2 comments:

Anonymous said...

It should be noted that this is only broken in versions of Java less then or equal to 1.4.

In Java 5+ you simply have to add volatile to 'helper' and it will work as expected.

Dmitri said...

You are right, locking semantics has been changed in Java 5 to cover ordering of operations.

Declaring a member variable 'volatile' will work, although alightly slower compared to having an internal class (not that it matters).

 
Hit Counter