2010-11-02 40 views
3

在java中是以下Thead Safe?信號量能否安全使用雙重鎖定成語?

public class TestDCL{ 
    private static final Semaphore lock = new Semaphore(1); 
    private Object instance; 

    public Object m(){ 
     if(instance == null){ 
      lock.acquire(); 
      if(instance == null){ 
       instance = new Object(); 
      } 
      lock.release(); 
     } 
     return instance; 
    } 
} 

回答

0

答案是,這取決於。你的instance是可變的還是不可變的?如果instance包含可變狀態,則必須將其聲明爲volatile。你必須這樣做的原因與@Vijay Mathew建議的部分構造對象無關,而是與線程可見性有關。在一個線程中對單例實例的狀態所做的更改不一定對另一個線程可見。使用volatile關鍵字可確保硬件將使用某種機制來刷新緩存以消除陳舊的數據。

Java併發實踐,第16.2.3章概述了安全初始化習語,並且包含一個懶惰初始化習語。 16.2.4討論了複查鎖定習語。

+0

「volatile」解決方案僅適用於J2SE5及更高版本。最可移植的(即跨幾個JVM版本)解決方案仍然使用靜態類來完成初始化。 – 2010-11-03 05:47:34

2

這不是線程安全的。聲明new Object();不是原子操作。當爲其分配內存時,instance不再是null。在內存分配給instance之後,但在其構造函數被調用之前,將到達第一個if條件的新線程將返回一個部分構造的對象。如果您試圖實現線程安全單例,請使用線程安全和懶惰的Bill Pugh's solution

+0

-1:您關於'new'如何工作和變量賦值不正確的陳述。 – 2010-11-02 22:49:32

+0

我不是Java專家,但我相信Vijay的陳述是正確的。根據Java的內存模型,構造函數運行前''instance''肯定可以被分配**。當然,它不可能是原子的,因爲可以在構造函數中執行任意數量的操作。 – 2010-11-03 02:11:22

+0

@Brian Gideon,我很肯定,即使在創建新對象時,Java仍然遵循表達式評估的基本規則。也就是說,對於'=',在評估操作符之前,必須評估右邊的所有內容。包括,分配空間,初始化變量和運行構造函數代碼。 http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html – 2010-11-03 08:08:05

1

同意Tim以前的帖子。易失性使可見性和原因雙重檢查鎖定已被描述爲clever but broken是圍繞部分構建的對象(緩存一致性/ JVM優化)。

這一切都在戈茨的書中,因爲蒂姆建議,但我想提出一個懶惰初始化的觀點。爲什麼這樣做?根據我的經驗,它通常不需要,它運行在多線程的環境中,並且真正關心初始化安全性 - 您已經引入了很多可變性和複雜性,這很難測試。

我還強調舊的警告,不提前優化。您知道粗粒度同步會降低應用程序的速度嗎?通常,它的鎖的爭用速度很慢,而不是同步關鍵字per-sa。同步化和DCL的快速測試將得到證實。