...
Wiki Markup |
---|
"Today, the double-check idiom is the technique of choice for lazily initializing an instance field. While you can apply the double-check idiom to {{static}} fields as well, there is no reason to do so: the lazy initialization holder class idiom is a better choice." \[[Bloch 08|AA. Java References#Bloch 08]\]. |
Compliant Solution (
...
static initialization)
Wiki Markup |
---|
This compliant solution initializes the {{helper}} in the declaration of the {{static}} variable \[[Manson 06|AA. Java References#Manson 06]\]. |
In this solution the Foo
class is unchanged, but the Helper
class is immutable. In this case, the Helper
class is guaranteed to be fully constructed before becoming visible. The object must be truly immutable; it is not sufficient for the program to refrain from modifying the object. Code Block |
---|
|
public class HelperFoo {
private static final Helper int nhelper = new Helper(42);
public static Helper getHelper(int n) {
this.n = n;
}
// other fields & methods,return helper;}
}
|
Variables declared static
are guaranteed to be initialized and made visible to other threads immediately. Static initializers also exhibit these properties. This approach should not be confused with eager initialization because in this case, the Java Language Specification guarantees lazy initialization of the class when it is first used.
Compliant Solution (initialize-on-demand holder class idiom)
This compliant solution explicitly incorporates lazy initialization. It also uses a static
variable as suggested in the previous compliant solution. The variable is declared within a static
inner, Holder
class.
Code Block |
---|
|
all fields are final
}
class Foo {
privatestatic Helper helper = null;
public// HelperLazy getHelper()initialization {
if (helper == null) {
synchronized(this)private static class Holder {
static Helper helper = ifnew Helper(helper == null) {);
}
public static Helper getInstance() {
helper = new Helper(); // If the helper is null, create a new instance
}
}
}
return helper; // If helper is non-null, return its instance
}
}
|
Note that if Foo
was mutable, the Helper
field would need to be declared volatile
as shown in CON00-J. Declare status flags as volatile. 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-constructed objects).
...
return Holder.helper;
}
}
|
Wiki Markup |
---|
This idiom is called the initialize-on-demand holder 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]\]. |
Compliant Solution (ThreadLocal
storage)
Wiki Markup |
---|
This compliant solution initializes(originally the {{helper}} in the declaration of the {{static}} variablesuggested by Alexander Terekhov \[[MansonPugh 0604|AA. Java References#MansonReferences#Pugh 0604]\]. |
...
...
...
...
uses a {{ThreadLocal}} object to lazily create a {{Helper}} instance. |
Code Block |
---|
|
class Foo {
// If perThreadInstance.get() returns a non-null value, this thread
// has done synchronization needed to see initialization of helper
private final ThreadLocal perThreadInstance = new ThreadLocal();
private Helper helper = null;
public Helper getHelper() {
if (perThreadInstance.get() == null) |
Variables declared static
are guaranteed to be initialized and made visible to other threads immediately. Static initializers also exhibit these properties. This approach should not be confused with eager initialization because in this case, the Java Language Specification guarantees lazy initialization of the class when it is first used.
Compliant Solution (initialize-on-demand holder class idiom)
This compliant solution explicitly incorporates lazy initialization. It also uses a static
variable as suggested in the previous compliant solution. The variable is declared within a static
inner, Holder
class.
Code Block |
---|
|
class Foo {
static Helper helper;
// Lazy initialization
private static class Holder {
static Helper helper = new Helper createHelper();
}
public static Helper getInstance() {}
return Holder.helper;
}
}
|
Wiki Markup |
---|
This idiom is called the initialize-on-demand holder 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]\]. |
Compliant Solution (ThreadLocal
storage)
Wiki Markup |
---|
This compliant solution (originally suggested by Alexander Terekhov \[[Pugh 04|AA. Java References#Pugh 04]\]) uses a {{ThreadLocal}} object to lazily create a {{Helper}} instance. |
}
private final void createHelper() {
synchronized(this) {
if (helper == null) {
helper = new Helper();
}
// Any non-null value would do as the argument here
perThreadInstance.set(perThreadInstance);
}
}
}
|
Compliant Solution (immutable
)
In this solution the Foo
class is unchanged, but the Helper
class is immutable. In this case, the Helper
class is guaranteed to be fully constructed before becoming visible. The object must be truly immutable; it is not sufficient for the program to refrain from modifying the object.
Code Block |
---|
|
public class Helper {
private final int n;
public Helper(int n) {
this.n = n;
}
// other fields & methods, all fields are final
}
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this |
Code Block |
---|
|
class Foo {
// If perThreadInstance.get() returns a non-null value, this thread
// has done synchronization needed to see initialization of helper
private final ThreadLocal perThreadInstance = new ThreadLocal();
private Helper helper = null;
public Helper getHelper() {
if (perThreadInstance.get()helper == null) {
createHelper();
}
helper = return helper;
}
private final void createHelper() {
synchronized(this) {new Helper(); // If the helper is null, create a new instance
}
if}
(helper == null) {}
return helper; // If helper = new Helper();
}
// Any non-null value would do as the argument here
perThreadInstance.set(perThreadInstance);
}
}
}
is non-null, return its instance
}
}
|
Note that if Foo
was mutable, the Helper
field would need to be declared volatile
as shown in CON00-J. Declare status flags as volatile. 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-constructed objects).
Exceptions
EX1: Explicitly synchronized code (that uses method synchronization or proper block synchronization, that is, enclosing all initialization statements) does not require the use of double-checked locking.
Wiki Markup |
---|
*EX2:* "Although the double-checked locking idiom cannot be used for references to objects, it can work for 32-bit primitive values (e.g., int's or float's). Note that it does not work for long's or double's, since unsynchronized reads/writes of 64-bit primitives are not guaranteed to be atomic." \[[Pugh 04|AA. Java References#Pugh 04]\]. See [CON25-J. Ensure atomicity when reading and writing 64-bit values] for more Informationinformation. |
Risk Assessment
Using incorrect forms of the double checked locking idiom can lead to synchronization issues.
...