Wiki Markup |
---|
The usual way of initializing an object is to use a constructor. Sometimes it is required to limit the number of instances of a member object to just one (this is similar to a singleton, however, the member object may or may not be {{static}}). Instead of initializing using a constructor, sometimes a technique called lazy initialization iscan be used to defer the construction of the member object until an itinstance is actually required. Reasons for incorporating lazyLazy initialization includealso optimizinghelps andin breaking harmful circularities in class and instance initialization, and performing other optimizations \[[Bloch 05|AA. Java References#Bloch 05]\]. |
A class or an instance method is frequently used for lazy initialization, depending on whether the member object is static
or not. The method checks whether the instance has already been created and if not, creates it. If the instance already exists, it simply returns it. This is shown below:
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; } // ... } |
In a multi-threading scenariomultithreading scenario, the initialization must be synchronized so that two or more threads do not create multiple instances of the member object. The code shown below is safe for execution in a multithreaded environment, albeit slower than the previous, single threaded code example.
...
The double checked locking (DCL) idiom is used to provide lazy initialization in multithreaded code. In a multi-threading multithreading scenario, traditional lazy initialization is supplemented by reducing the cost of synchronization for each method access by limiting the synchronization to the case where the instance is required to be created and forgoing it when retrieving an already created instance.
The double-checked locking pattern eliminates method synchronization and uses block synchronization instead of method synchronization. It strives to make the previous code example faster by installing a null
check before attempting to synchronize. The pattern strives to make the previous code example faster by installing a null
check before attempting to synchronize. This makes expensive synchronization necessary only for initialization, and dispensable for the common case of retrieving the value of helper
. The noncompliant code example shows the originally proposed DCL pattern.
...
This makes the originally proposed double-checked locking pattern insecure. The guideline CON26-J. Do not publish partially initialized objects further discusses the possible occurrence of a non-null reference to a helper
Helper
object that observes default values of fields in the helper
Helper
object.
Compliant Solution (volatile
)
...
Wiki Markup |
---|
This compliant solution initializes the {{helper}} field in the declaration of the {{static}} variable \[[Manson 06|AA. Java References#Manson 06]\]. |
Code Block | ||
---|---|---|
| ||
final class Foo { private static final Helper helper = new Helper(); public static Helper getHelper() { return helper; } } |
Wiki Markup |
---|
Variables declared {{static}} are guaranteed to be initialized and instantly made visible to other threads. Static initializers also exhibit these properties. This approach should not be confused with eager initialization because in this case, the Java Language Specification \[[JLS 05|AA. Java References#JLS 05]\] guarantees lazy initialization of the class when it is first used. |
Compliant Solution (initialize-on-demand holder class idiom)
This compliant solution explicitly uses the initialize-on-demand holder class idiom that implicitly incorporates lazy initialization. It uses a static
variable as suggested in the previous compliant solution. The variable is declared within a static
inner class, Holder
.
Code Block | ||
---|---|---|
| ||
final class Foo {
static Helper helper;
// Lazy initialization
private static class Holder {
static Helper helper = new Helper();
}
public static Helper getInstance() {
return Holder.helper;
}
}
|
...
} } |
Wiki Markup |
---|
class idiom. Initialization of the {{Holder}} class is deferred until the {{getInstance()}} method is called, following which the {{helper}} is initialized. The only limitation of this method is that it works only for {{static}} fields and not instance fields \[[Bloch 01|AA. Java References#Bloch 01]\]. This idiom is a better choice than the double checked locking idiom for lazily initializing {{static}} fields \[[Bloch 08|AA. Java References#Bloch 08]\]. |
...
This compliant solution uses an AtomicReference
wrapper around the Helper
object. It uses the standard compareAndSet
(CAS) functionality to set a newly created Helper
object if helperRef
is null
. Otherwise, it simply returns the already created instance. (Tom Hawtin, JMM Mailing List)
Code Block | ||
---|---|---|
| ||
// Uses atomic utilities final class Foo { private final AtomicReference<Helper> helperRef = new AtomicReference<Helper>(); public Helper getHelper() { Helper helper = helperRef.get(); if (helper != null) { return helper; } Helper newHelper = new Helper(); return helperRef.compareAndSet(null, newHelper) ? newHelper : helperRef.get(); } } |
Note that while While this code ensures that only one Helper
object is preserved, it may potentially allow multiple Helper
objects to be created; , with all but one being garbage-collected. However, if constructing multiple Helper
objects is infeasible or expensive, this solution may be inappropriate.
...
Note that if class Foo
were mutable, the Helper
field would need to be declared volatile
as shown in CON09-J. Ensure visibility of shared references to immutable objects. Also, the method getHelper()
is an instance method and the accessibility of the helper
field is private
. This allows safe publication of the Helper
object, in that, a thread cannot observe a partially initialized Foo
object (CON26-J. Do not publish partially initialized objects). The class Helper
is also compliant with CON26-J. Do not publish partially initialized objects and consequently, cannot be observed to be in a partially initialized state.
...
Wiki Markup |
---|
\[[API 06|AA. Java References#API 06]\] \[[JLS 05|AA. Java References#JLS 05]\] 12.4 "Initialization of Classes and Interfaces" \[[Pugh 04|AA. Java References#Pugh 04]\] \[[Bloch 01|AA. Java References#Bloch 01]\] Item 48: "Synchronize access to shared mutable data" \[[Bloch 08|AA. Java References#Bloch 08]\] Item 71: "Use lazy initialization judiciously" \[[MITRE 09|AA. Java References#MITRE 09]\] [CWE ID 609|http://cwe.mitre.org/data/definitions/609.html] "Double-Checked Locking" |
...