On Wed, Jun 4, 2008 at 9:00 AM, Bruno Harbulot <
[EMAIL PROTECTED]> wrote:
> What I'm less clear about is the benefits of the double-check locking (DLC)
> pattern. I think the intent behind this pattern was to improve performance,
> but was in fact broken until the Java 5 memory model (and the use of
> 'volatile', which is done here).
>
> I don't think 'volatile' and the DCL are necessary, they could be replaced
> with a simple lock. I doubt they really improve performance, since
> 'volatile' effectively has some synchronisation overhead anyway. The DCL
> could be removed to have a simple "synchronized(this) { if (field==null) ...
> }". 'volatile' could be removed as well, if the other accessors were also
> synchronised (probably better).
>
Josh Bloch has a nice presentation of the tradeoffs in Effective Java, 2nd
edition, Item 71. I'll try to summarize briefly.
First, and most important, don't use lazy initialization unless you need to.
The main reasons you might need to are: performance (when initializing an
instance is very expensive and the need for initialization is relatively
rare among instances) and to break initialization circularities.
If you need to break initialization circularities, use a *synchronized
accessor*. It's simple and reasonably fast:
public synchronized Foo getFoo() {
if (foo == null) {
foo = initialValueForFoo();
}
return foo;
}
If you have a static field that needs to be initialized lazily for
performance reasons, use the *lazy initialization holder **class **idiom*.
private static class FooHolder {
static final Foo value = initialValueForFoo();
}
public static Foo getFoo() { return FooHolder.value; }
If you have an instance field that needs to be initialized lazily for
performance reasons, use the *double-check **idiom*.
public Foo getFoo() {
Foo result = this.foo;
if (result == null) {
synchronized (this) {
result = this.foo;
if (result == null) {
this.foo = result = initialValueForFoo();
}
}
}
return result;
}
private volatile Foo foo;
This last one is somewhat delicate because it involves reasoning about the
memory effects of volatile reads and writes -- don't deviate from the
pattern above. But it can be significantly faster than the synchronized
accessor on modern multiprocessor architectures, and it is highly unlikely
to be worse.
--tim