Why is double-checked locking broken in Java?
Several assignments may be needed to construct the instance of Helper
inside the constructor, and the semantics allows that they are reordered with respect to the assignment helper = new Helper()
.
So the field helper
may be assigned a reference to an object where not all assignments have taken place, so that it is incompletely initialized.
The problem is not atomicity, it's ordering. The JVM is allowed to reorder instructions in order to improve performance, as long as happens-before is not violated. Therefore, the runtime could theoretically schedule the instruction that updates helper
before all instructions from the constructor of class Helper
have executed.
The assignment of the reference is atomic, but the construction is not! So as stated in the explanation, supposing thread B wants to use the singleton before Thread A has fully constructed it, it cannot create a new instance because the reference is not null, so it just returns the partially constructed object.
If you do not ensure that publishing the shared reference happens before another thread loads that shared reference, then the write of the reference to the new object can be reordered with the writes to its fields. In that case, another thread could see an up-to-date value for the object reference but out of date values for some or all of the object's state - a partially constructed object. -- Brian Goetz: Java Concurrency in Practice
Since the initial check for null is not synchronized there is no publication and this reordering is possible.
Double checked locking in java has a variety of problems:
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html