0

我一直在使用LazyReference類幾年(當然不定期,但有時它是非常有用的)。這個班級可以看到here。學分由Robbie Vanbrabant(班級作者)和Joshua Bloch着名的「Effective Java 2nd edt」完成。 (原始碼)。LazyReference與雙重檢查鎖定和空處理

該類可正常工作(在Java 5+中),但存在一個小小的潛在問題。如果instanceProvider返回null(當然,它不能根據Guice Provider.get()合同,但是...),然後在每次執行LazyReference.get()方法時,LOCK將被保留並且instanceProvider.get將被一次又一次地調用。對於那些違反合同的人來說,這看起來是一種很好的懲罰(但他),但如果真的需要懶惰地初始化一個領域並設置價值的可能性呢?

我修改LazyReference一點點:

public class LazyReference<T> { 

    private final Object LOCK = new Object(); 

    private volatile T instance; 

    private volatile boolean isNull; 

    private final Provider<T> instanceProvider; 

    private LazyReference(Provider<T> instanceProvider) { 
    this.instanceProvider = instanceProvider; 
    } 

    public T get() { 
    T result = instance; 
    if (result == null && !isNull) { 
     synchronized (LOCK) { 
     result = instance; 
     if (result == null && !isNull) { 
      instance = result = instanceProvider.get(); 
      isNull = (result == null); 
     } 
     } 
    } 
    return result; 
    } 
} 

恕我直言應該只是罰款(如果你有另一種意見,請發表您的意見和批評)。但我想知道如果我從isNull布爾值中刪除volatile修飾符會發生什麼?(當然,將它留給instance)?它會繼續正常工作嗎?

回答

2

正如尼爾·科菲指出,該代碼包含一個競爭條件,但它可以如下容易固定的(注意,instance不需要是volatile):

public class LazyReference<T> {  
    private T instance; 
    private volatile boolean initialized; 
    ... 
    public T get() { 
    if (!initialized) { 
     synchronized (LOCK) { 
     if (!initialized) { 
      instance = instanceProvider.get(); 
      initialized = true; 
     } 
     } 
    } 
    return instance; 
    } 
} 
+0

'實例不需要易變 - - 好點!由於'instance'寫在volatile初始化字段之前,所有其他讀取'initialized'的線程都會看到它。 – Idolon

3

上面的代碼有一個競爭條件:在isNull被設置之前,實例可能被設置爲instanceProvider.get()的結果中的「real」null。

您是否確定要放棄這些複雜的廢話並進行正確的同步並不容易?我敢打賭,你將無法衡量任何性能差異,並且更容易驗證你的代碼是否正確。

+0

是的,你是對的: 'isNull'設置爲'true'之前'instance'可以設置爲'real''null'。但在這種情況下,執行將通過同步塊,其中'isNull'值將已經等於'true'。然後第二個條件檢查將評估爲「false」,並且'instanceProvider.get'不會被調用多次。或者我錯過了什麼? – Idolon

+0

回答你的問題:是的,使用簡單同步或者使用原始的'LazyReference'(如果一個人遵守提供者合同,這將工作得非常完美)會更容易。問題的目的是個人理論上的興趣。 ** axtavt **已經發布了比我的更簡單和優雅的解決方案 - 好吧,練習會更多。 – Idolon