The double-checked locking idiom is a software design pattern used to reduce the overhead of acquiring a lock by first testing the locking criterion without actually acquiring the lock. Double-checked locking improves performance by limiting synchronization to the rare case of computing the field's value or constructing a new instance for the field to reference and by foregoing synchronization during the common case of retrieving an already-created instance or value.
Incorrect forms of the double-checked locking idiom include those that allow publication of an uninitialized or partially initialized object. Consequently, only those forms of the double-checked locking idiom that correctly establish a happens-before relationship both for the helper
reference and for the complete construction of the Helper
instance are permitted.
The double-checked locking idiom is frequently used to implement a Singleton Factory pattern that performs lazy initialization. Lazy initialization defers the construction of a member field or an object referred to by a member field until an instance is actually required rather than computing the field value or constructing the referenced object in the class's constructor. Lazy initialization helps to break harmful circularities in class and instance initialization . It also enables other optimizations [Bloch 2005].
...
Code Block | ||
---|---|---|
| ||
// Correct multithreaded version using synchronization final class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) { helper = new Helper(); } return helper; } // ... } |
The double-checked locking idiom improves performance by limiting synchronization to the rare case of computing the field's value or constructing a new instance for the field to reference and by foregoing synchronization during the common case of retrieving an already-created instance or value.
Incorrect forms of the double-checked locking idiom include those that allow publication of an uninitialized or partially initialized object. Consequently, only those forms of the double-checked locking idiom that correctly establish a happens-before relationship both for the helper
reference and for the complete construction of the Helper
instance are permitted.
Noncompliant Code Example
...
In this noncompliant code example, we modify the the Helper
class to be is made immutable , by declaring its fields final. The Java Memory Model (JMM) guarantees that immutable objects are fully constructed before they become visible to any other thread. The block synchronization in the getHelper()
method suffices to ensure method guarantees that all threads that can see a non-null value of the helper field will see the fully-initialized Helper
object.
Code Block | ||||
---|---|---|---|---|
| ||||
public final class Helper { private final int n; public Helper(int n) { this.n = n; } // Other fields and methods, all fields are final } final class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { // 1stFirst read of helper synchronized (this) { if (helper == null) { // 2ndSecond read of helper helper = new Helper(42); } } } return helper; // 3rdThird read of helper } } |
However, this code is still unsafe on some not guaranteed to be succeed on all JVM platforms . This occurs because there is no happens-before relationships on the 1st read or the 3rd between the first read and third read of helper
. ThereforeConsequently, it is possible for the 3rd third read of helper
to obtain a stale null value of null (perhaps because its value was cached or reordered by the compiler) . In other words, a call to causing the getHelper()
can method to return a null pointer.
Compliant Solution (Immutable)
This compliant solution uses a local variable to reduce the number of reads of the helper
field to 1. ThereforeAs a result, if the read of helper
yields a non-null value, it is cached in a local variable that is inaccessable to other threads, and safely returned.
...