I'm working my way through item 71, "Use lazy initialization judiciously", of Effective Java (second edition). It suggests the use of the double-check idiom for lazy initialization of instance fields using this code (pg 283):
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;
}
So, I actually have several questions:
Why is the volatile modifier required on
field
given that initialization takes place in a synchronized block? The book offers this supporting text: "Because there is no locking if the field is already initialized, it is critical that the field be declared volatile". Therefore, is it the case that once the field is initialized, volatile is the only guarantee of multiple thread consistent views onfield
given the lack of other synchronization? If so, why not synchronize getField() or is it the case that the above code offers better performance?The text suggests that the not-required local variable,
result
, is used to "ensure thatfield
is read only once in the common case where it's already initialized", thereby improving performance. Ifresult
was removed, how wouldfield
be read multiple times in the common case where it was already initialized?