2009-04-22 23 views
1

假設這是一個「單例」實現:我保證這隻會調用一次productCatalogLoader.load(),並且不會出現任何空指針?任何方法來使這更簡單?使用沒有鎖定的writeLock安全嗎?

private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); 
private ProductCatalog productCatalog; 

public ProductCatalog get() { 
     if (this.productCatalog == null) { 
      reload(); 
     } 
     return this.productCatalog; 
}               

public void reload() { 

    lock.writeLock().lock(); 
    try { 
     if (this.productCatalog != null) return; 
     this.productCatalog = productCatalogLoader.load(); 
    } finally { 
     lock.writeLock().unlock(); 
    } 
} 

}

編輯: 這是一個謙虛成功的嘗試減少更爲複雜的代碼,以一個簡單的問題樣本。有幾個人注意到我的單例過於複雜的事實;)(實際上也有一個石英計時器調用reload,但是「重新加載」實現與IRL有點不同)。無論如何,我得到了我所需要的答案,破壞了雙重鎖定。

回答

2

這看起來像broken double checked locking。問題是this.productCatalog可以在對象完全構造之前分配。

說,我們有

var foo = new ProductCatalog(); 
foo.blah = "blah blah"; 
this.productCatalog = foo; 

的可重新排序

var foo = new ProductCatalog(); 
this.productCatalog = foo; 
foo.blah = "blah blah"; 
+0

關鍵詞是「不安全的出版物」。 – 2009-04-22 12:32:32

4

,最好的辦法,使其更簡單,國際海事組織,是使用一個「正常」的鎖,除非你真的有很好的證據表明,它造成了瓶頸:

private final Object lock = new Object(); 
private ProductCatalog productCatalog; 

public ProductCatalog get() { 
    synchronized (lock) { 
     if (this.productCatalog == null) { 
      this.productCatalog = productCatalogLoader.load(); 
     } 
     return this.productCatalog; 
    } 
}               

在絕大多數情況下,這將足夠好,你不需要擔心內存模型的技術問題。

編輯:至於是否讀取寫入鎖中未更改讀取鎖的數據是安全的 - 我嫌疑人不是,但我不想肯定地說。使用正常的方法(如果您小心的話,在Java 1.5+上安全)使用volatile變量進行雙重檢查鎖定有什麼好處嗎?

private final Object lock = new Object(); 
private volatile ProductCatalog productCatalog; 

public ProductCatalog get() { 
    if (this.productCatalog == null) { 
     synchronized (lock) { 
      if (this.productCatalog == null) { 
       this.productCatalog = productCatalogLoader.load(); 
      } 
     } 
    } 
    return this.productCatalog; 
}               

我相信這是有效的Java第二版建議延遲初始化技術,對於靜態初始化是不夠的情況。請注意,productCatalog必須是易失性的,才能正常工作 - 我認爲這是有效由於沒有取出代碼中的讀鎖,所以缺少了什麼。

+0

嗯,我傾向於同意。但是,如果不使用readLock,可以安全地使用writeLock嗎? (可以說更加複雜,是的) – krosenvold 2009-04-22 06:39:09

3

不,一般來說它是不安全的...寫鎖只能保證沒有其他人能夠獲得一個readlock或writelock,直到writeelock被釋放。 沒有在你的代碼中使用readlock就像重載方法是同步的,但不是get方法。我有點贊同Jon Skeet的觀點,但這取決於使用情況。

如果你有很多線程調用的時間差不多,你可能需要一個readlock而不是同步它。

編輯:順便說一句,關於如何在Effective Java 2nd Edition中做到這一點,我有一個很好的例子... 我也必須在這裏糾正自己,鑑於上下文,同步如果在智能方式(在這種情況下writeelock可能也會起作用)。

+0

在那時,正常的DCL可能會更快。 – 2009-04-22 06:50:25

0

是不是很簡單,如果你想做一個單做:

private final ProductCatalog productCatalog = productCatalogLoader.load(); 
public ProductCatalog get() { 
    return this.productCatalog; 
} 

如果有一些原因,你不能讓ProductCatalog最後的成員,則無論是使用每喬恩同步只要通過get()調用或reload()函數訪問成員productCatalog,Skeet的答案或您的原始代碼應該可以工作。