22 March, 2014

Lazy initialization by Joshua Bloch

   The most natural way to add lazy initialization is to create method checking if object's field was initialized and if not then do it.
    private SomeObject obj;

    public SomeObject getObject() {
        if (obj == null)
            obj = computeObjectValue();
        return obj;
    } 
Such a code is pretty simple, but requires synchronization in case of concurrent access. Adding synchronized keyword to getObject method is sufficient to avoid repeated initialization for instance and static fields, but harms performance. That's why it is good to use several idioms proposed by Joshua Bloch.

Single-check idiom
    private volatile SomeObject obj;
    
    public SomeObject getObject() {
        SomeObject result = obj;
        if(result == null) 
            obj = result = computeObjectValue();
        return result;
    }
When to use:  if an instance field can tolerate repeated initialization
Why field needs to be volatile:  it adds synchronization on the field; in case of primitives other than long or double volatile can be removed, but in this situation we will face more repeated initializations caused by caching field value by VM
Why to use additional local variable result:  access to volatile field is slower than to this variable

Double-check idiom
    private volatile SomeObject obj;

    public SomeObject getObject() {
        SomeObject result = obj;
        if (result == null) {
            synchronized (this) {
                result = obj;
                if (result == null)
                    obj = result = computeObjectValue();
            }
        }
        return result;
    }
When to use:  if an instance field should be initialized only once.
Why double check field:  in this solution we postpone synchronization to the very last moment, first check is outside synchronized block, so we need to do it again.

Holder class idiom
    public static SomeObject getObject() {
        return FieldHolder.obj;
    }

    private static class FieldHolder {
        private static final SomeObject obj = computeObjectValue();
    }
When to use:  if you want to lazy initialize static field
Why to use additional class:  it is guaranteed that classes will not be initialized until it is used. VM synchronize field access only to initialize the class. When field is subsequently accessed VM patches the code, so no testing or synchronization is involved.

No comments:

Post a Comment