歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Effective Java - 延遲初始化

延遲初始化(lazy initialization),也就是在真正被使用的時候才開始初始化的技巧。
 不論是靜態還是實例,都可以進行延遲初始化。
 其本質是初始化開銷和訪問開銷之間的權衡。
 畢竟是一種優化技巧,使用不當會起反效果。
 尤其是在多線程場景中這種反效果會尤為明顯,因為我們要對這個進行延遲初始化的field進行同步。

先一步步開始,如果初始化開銷不值一提,我們只需要保證其不可變即可:
private final FieldType field1 = computeFieldValue();

 

如果還有的商量,初始化開銷可能讓人在意,下面是最簡單的的方式,直接在訪問方法聲明裡加了synchoronized修飾,這種方式將訪問開銷最大化了:
private FieldType field2;

synchronized FieldType getField2() {
    if (field2 == null)
        field2 = computeFieldValue();
    return field2;
}

    private static FieldType computeFieldValue() {
    return new FieldType();
}

 

如果要改為靜態的也不過是加上static修飾,但對於靜態初始化,我們可以使用class holder方式:
private static class FieldHolder {
    static final FieldType field = computeFieldValue();
}

static FieldType getField3() {
    return FieldHolder.field;
}

private static FieldType computeFieldValue() {
    return new FieldType();
}

 

這種方式感覺不錯,我們沒有進行額外的同步處理,只有在訪問getField3的時候FieldHolder才會被初始化。
 所以這種情況屬於沒有增加訪問開銷也保證了延遲特性。

這次試試優化一下實例field的訪問開銷,最經典的就是double-check了,這個東西經常出現在筆試題中:
private volatile FieldType field4;

FieldType getField4() {
    FieldType result = field4;
    if (result == null) {
        synchronized (this) {
            result = field4;
            if (result == null)
                field4 = result = computeFieldValue();
        }
    }
    return result;
}
private static FieldType computeFieldValue() {
    return new FieldType();
}

 

代碼中使用了result局部變量,這樣做雖然不是必要的,但這樣可以確保field已被初始化的情況下被讀取一次,可以提高少許效率。

以上就是延遲初始化的一些常用方式。
 延遲初始化看起來不錯,但建議權衡訪問和創建的開銷,對於實例field使用double-check,對於靜態field使用holder class,以在多線程訪問時保證check-then-action的原子性。

Copyright © Linux教程網 All Rights Reserved