Lazy
initialization is the act of delaying the initialization of a field until its
value is needed. If the value is never needed, the field is never initialized.
This technique is applicable to both static and instance fields. While lazy
initialization is primarily an optimization, it can also be used to break
harmful circularities in class and instance initialization.
As is the case
for most optimizations, the best advice for lazy initialization is “don’t do it
unless you need to” (Item 55). Lazy initialization is a double-edged sword. It
decreases the cost of initializing a class or creating an instance, at the
expense of increasing the cost of accessing the lazily initialized field.
Depending on what fraction of lazily initialized fields eventually require
initialization, how expensive it is to initialize them, and how often each
field is accessed, lazy initialization can (like many “optimizations”) actually
harm performance.
That said,
lazy initialization has its uses. If a field is accessed only on a fraction of
the instances of a class and it is costly to initialize the
field, then lazy initialization may be worthwhile. The only way to know for
sure is to measure the performance of the class with and without lazy
initialization.
In the
presence of multiple threads, lazy initialization is tricky. If two or more
threads share a lazily initialized field, it is critical that some form of
synchronization be employed, or severe bugs can result (Item 66). All of the
initialization techniques discussed in this item are thread-safe.
Under most
circumstances, normal initialization is preferable to lazy initialization. Here is a
typical declaration for a normally initialized instance field. Note the use of
the final modifier (Item
15):
// Normal initialization of an instance field
private final FieldType field = computeFieldValue();
If you use lazy
initialization to break an initialization circularity, use a synchronized
accessor, as it is the simplest, clearest alternative:
// Lazy initialization of instance field -
synchronized accessor
private FieldType field;
synchronized FieldType
getField() {
if (field == null)
field = computeFieldValue();
return field;
}
Both of these
idioms (normal initialization and lazy initialization with a synchronized
accessor) are unchanged when applied to static fields, except that you add
the static modifier to
the field and accessor declarations.
If you need to
use lazy initialization for performance on a static field, use the lazy
initialization holder class idiom. This idiom (also
known as the initializeon- demand holder class idiom) exploits the
guarantee that a class will not be initialized until it is used [JLS, 12.4.1].
Here’s how it looks:
// Lazy initialization holder class idiom for static
fields
private static class FieldHolder {
static final FieldType field = computeFieldValue();
}
static FieldType getField() { return FieldHolder.field;
}
When the getField method is invoked for the first time, it
reads Field- Holder.field for the first
time, causing the FieldHolder class to get
initialized. The beauty of this idiom is that the getField method is not synchronized and performs only
a field access, so lazy initialization adds practically nothing to the cost of
access. A modern VM will synchronize field access only to initialize the class.
Once the class is initialized, the VM will patch the code so that subsequent
access to the field does not involve any testing or synchronization.
If you need to
use lazy initialization for performance on an instance field, use the double-check
idiom. This idiom avoids the cost of locking when accessing the field
after it has been initialized (Item 67). The idea behind the idiom is to check
the value of the field twice (hence the name double-check): once
without locking, and then, if the field appears to be uninitialized, a second
time with locking. Only if the second
check indicates that the field is uninitialized does the call initialize the
field. Because there is no locking if the field is already initialized, it is critical that the field
be declared volatile (Item 66).
Here is the idiom:
// Double-check idiom for lazy initialization of
instance fields
private volatile
FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) { // First check (no locking)
synchronized(this) {
result = field;
if (result == null) // Second check (with locking)
field = result = computeFieldValue();
}
}
return result;
}
Prior to
release 1.5, the double-check idiom did not work reliably because the semantics
of the volatile modifier were
not strong enough to support it [Pugh01]. The memory model introduced in
release 1.5 fixed this problem [JLS, 17, Goetz06 16]. 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.
Two variants
of the double-check idiom bear noting. Occasionally, you may need to lazily
initialize an instance field that can tolerate repeated initialization. If you
find yourself in this situation, you can use a variant of the double-check
idiom that dispenses with the second check. It is, not surprisingly, known as
the singlecheck
idiom.
Here is how it looks. Note that field is still
declared volatile:
// Single-check idiom - can cause repeated
initialization!
private volatile
FieldType field;
private FieldType getField() {
FieldType result = field;
if (result == null)
field = result = computeFieldValue();
return result;
}
If you don’t
care whether every thread recalculates the value of a field, and the type of the
field is a primitive other than long or double, then you may choose to remove the volatile modifier from the field declaration in the
single-check idiom. This variant is known as the racy
single-check idiom.
In summary,
you should initialize most fields normally, not lazily. If you must initialize
a field lazily in order to achieve your performance goals, or to break a
harmful initialization circularity, then use the appropriate lazy
initialization technique. For instance fields, it is the double-check idiom;
for static fields, the lazy initialization holder class idiom. For instance
fields that can tolerate repeated initialization, you may also consider the
single-check idiom.
Reference: Effective Java 2nd Edition by Joshua Bloch