2009-12-10 57 views
3

我讀過所有關於雙重檢查鎖定修復程序如何工作的內容,我不喜歡延遲初始化,但能夠修復遺留代碼並且這樣的問題太誘人而不嘗試解決。我的例子: private int timesSafelyGotten = 0; private Helper helper = null;Java通過強制同步兩次檢查加鎖,可行嗎?

public getHelper() 
{ 
    if (timesSafelyGotten < 1) { 
     synchronized (this) { 
      if (helper == null) { 
       helper = new Helper(); 
      } else { 
       timesSafelyGotten++; 
      } 
     } 
    } 
    return helper; 
} 

這樣,同步代碼必須運行一次創建幫助,一旦當它得到首次所以理論上timesSafelyGotten不能遞增,直到它創造的助手同步碼後已經釋放鎖定和助手必須完成初始化。

我沒有看到任何問題,但它是如此簡單,似乎太好,不真實,你怎麼看?

迦勒詹姆斯·迪萊爾

+6

如果您閱讀(並瞭解)爲什麼雙重檢查鎖定被破壞的詳細解釋之一,您將明白爲什麼您的代碼也被破壞。 – 2009-12-10 05:33:34

+1

它也值得注意的是,隨着Java 5和易失性,你可以做不間斷的雙重檢查鎖定。 Java 4和更早的你的聲明確實如此。 – 2009-12-10 05:40:36

+2

還值得注意的是,使用J5並大大提高了無爭用鎖的性能,DCL幾乎總是過早的優化,不值得冒這個風險。 – 2009-12-10 06:16:53

回答

2

如果使用JDK5 +,使用java.util.concurrent中,你的情況可能AtomicInteger

這些實用程序是專門提供的,因爲沒有人可以理解低級別的線程同步原語足以使它們正常工作。

1

這並不好。就可以得到timeSafelyGotten> 1。實施例:

  1. 線程1檢查是否成功地和同步線 停止
  2. 線程2檢查是否在同步代碼成功和 停止。
  3. 線程3檢查是否成功,並且 停止在同步代碼上。
  4. 線程1落入同步塊, 創建助手並離開此塊。
  5. 線程2落入同步塊, 遞增timeSafelyGotten並離開此塊。
  6. 線程3落入同步塊, 遞增timeSafelyGotten並離開此塊。

所以timeSafelyGotten = 2

你應該增加一個檢查:

if (helper == null) { 
    helper = new Helper(); 
} else if (timesSafelyGotten < 1) { 
    timesSafelyGotten++; 
} 

或移動同步上:

synchronized(this) { 
    if (timeSafelyGotten < 1) { 
     ... 
    } 
} 

第一種方式是更好,因爲它不」每次使用同步。

還有一個提示:不要使用同步(這個),因爲有人也可以使用你的對象進行同步。使用特殊的私有對象爲內部同步:

classs MyClass { 
    private Object syncRoot = new Object(); 

    ... 
    synchronized(syncRoot) { 
     .... 
    } 
} 
+0

感謝您的快速回復,不是線程2和3被阻止在同步代碼之外(在步驟5),直到助手完成初始化?如果是這樣,那麼它的工作不是嗎?我也喜歡同步(syncRoot)的想法。 – 2009-12-10 06:34:26

+0

是的,他們被阻止,但他們在第一次檢查後被阻止,並在解除阻止後陷入同步塊。我已經重寫了這個例子,使之更加清晰。 – 2009-12-10 09:03:57

4

在沒有存儲器屏障(​​,volatile,或從java.util.concurrent當量),線程可看到另一個線程的動作發生在不同的順序出現在源代碼。

由於有上timesSafelyGotten讀取無記憶障礙,它可以出現另一個線程timesSafelyGotten遞增之前helper分配。這將導致從該方法返回null

實際上,在單元測試期間,這可能適用於許多架構。但它不正確,最終會失敗。

雙重鎖定確實現在正在工作,但實現正確和相當昂貴的技術很棘手。有一些延遲初始化模式不易脆弱,可讀性更強,而且不需要任何特殊的東西。

+0

您是否有鏈接到最新版本的Java中的惰性初始化模式的優秀教程? – 2009-12-10 07:25:11

+0

查看「初始化需求持有者」習慣用法:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom – erickson 2009-12-10 07:51:17

+0

我也喜歡IODH - 簡單,懶惰的初始化,快速且沒有同步問題。 – blackanchorage 2009-12-10 08:28:21