...
Lazy initialization uses either a class or an instance method, depending on whether the member object is static. The method checks whether the instance has already been created and, if not, creates it. When the instance already exists, the method simply returns the instance:
Code Block | ||
---|---|---|
| ||
// Correct single threaded version using lazy initialization
final class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
helper = new Helper();
}
return helper;
}
// ...
}
|
Lazy initialization must be synchronized in multithreaded applications to prevent multiple threads from creating extraneous instances of the member object:
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 pattern uses block synchronization rather than method synchronization and installs an additional null reference check before attempting synchronization. This noncompliant code example uses an incorrect form of the double-checked locking idiom.
Code Block | ||
---|---|---|
| ||
// "Double-Checked Locking" idiom
final class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
// Other methods and members...
}
|
...
This compliant solution declares the helper
field volatile.
Code Block | ||
---|---|---|
| ||
// Works with acquire/release semantics for volatile
// Broken under JDK 1.4 and earlier
final class Foo {
private volatile Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper();
}
}
}
return helper;
}
}
|
...
This compliant solution initializes the helper
field in the declaration of the static variable [Manson 2006].
Code Block | ||
---|---|---|
| ||
final class Foo {
private static final Helper helper = new Helper();
public static Helper getHelper() {
return helper;
}
}
|
...
This compliant solution uses the initialize-on-demand, holder class idiom that implicitly incorporates lazy initialization by declaring a static variable within a static Holder
inner class.
Code Block | ||
---|---|---|
| ||
final class Foo {
// Lazy initialization
private static class Holder {
static Helper helper = new Helper();
}
public static Helper getInstance() {
return Holder.helper;
}
}
|
...
This compliant solution (originally suggested by Alexander Terekhov [Pugh 2004]) uses a ThreadLocal
object to track whether each individual thread has participated in the synchronization that creates the needed happens-before relationships. Each thread stores a non-null value into its thread-local perThreadInstance
only inside the synchronized createHelper()
method; consequently, any thread that sees a null value must establish the necessary happens-before relationships by invoking createHelper()
.
Code Block | ||
---|---|---|
| ||
final class Foo {
private final ThreadLocal<Foo> perThreadInstance =
new ThreadLocal<Foo>();
private Helper helper = null;
public Helper getHelper() {
if (perThreadInstance.get() == null) {
createHelper();
}
return helper;
}
private synchronized void createHelper() {
if (helper == null) {
helper = new Helper();
}
// Any non-null value can be used as an argument to set()
perThreadInstance.set(this);
}
}
|
...
In this compliant solution, suppose that the Helper
class is immutable. The Java Memory Model (JMM) guarantees that immutable objects are fully constructed before they become visible to any other thread. Additionally, the block synchronization in the getHelper()
method suffices to ensure that all methods that can see a non-null value of the helper
field have a proper happens-before relationship for the update to the helper
reference. This synchronization and the aforementioned JMM guarantee combine to ensure that only fully initialized Helper
objects are visible to threads that see non-null values. Consequently, this compliant solution correctly creates both of the needed happens-before relationships.
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) {
synchronized (this) {
if (helper == null) {
helper = new Helper(42);
}
}
}
return helper;
}
}
|
...
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
LCK10-J | low | probable | medium | P4 | L3 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Coverity | 7.5 | DOUBLE_CHECK_LOCK FB.DC_DOUBLECHECK | Implemented |
Related Guidelines
...
[API 2006] |
|
Item 48. Synchronize access to shared mutable data | |
Item 71. Use lazy initialization judiciously | |
[JLS 2005] | §12.4, Initialization of Classes and Interfaces |
|