2011-12-22 97 views
0
在辛格爾頓

雙鎖檢查通常寫爲:Singleton設計模式

public static Singleton getInstance() 
{ 
    if (instance == null) 
    { 
     synchronized(Singleton.class) { //1 
      if (instance == null)   //2 
        instance = new Singleton(); //3 
     } 
    } 
    return instance; //4 
} 

在上面的代碼中,假設10個線程調用這個方法,所有的人都越過了第一個if條件,那麼一個線程進入同步塊並創建實例。即使創建了實例,剩餘的9個線程也會一個接一個地等待,並通過synchronized塊順序進入。我希望一旦任何線程創建Singleton實例,所有其他線程都不應該等待。告訴我是否有解決方案?

+1

Bill Pugh是你的英雄:http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:03:16

+0

@Mitch:那篇文章已經9歲了。雙重檢查鎖定可以在任何非古董虛擬機中安全工作;但它不是實例化單例的最佳方式。 – 2011-12-22 06:11:53

+0

@Mitch:啊!這太糟糕了,只要你知道它今天不是直接適用的,它仍然是一個很好的和信息豐富的閱讀。這裏又是:http://www.ibm.com/developerworks/java/library/j-dcl/index.html。隨後討論了在新內存模型下安全雙重檢查鎖定的缺點:http://www.ibm.com/developerworks/library/j-jtp03304/#3.2 – 2011-12-22 06:22:59

回答

8

如果你堅持使用懶惰實例,我不認爲有一個解決方案。 你可以只創建單獨的對象,當你聲明instance變量:

class Singleton { 
    private static final instance = new Singleton(); 

    private Singleton() {} // prevent outside construction 

    public static Singleton getInstance() { 
     return instance; // no synchronization needed 
    } 
} 

多虧了eSniff註釋(由亞伊爾註釋,並把我說得對eSniff的評論),這裏是在Wikipedia公佈的方法一個線程安全的,懶惰的方法:

class Singleton { 
    private static class Holder { 
     static final instance = new Singleton(); 
    } 

    private Singleton() {} // prevent outside construction 

    public static Singleton getInstance() { 
     return Holder.instance; // no synchronization needed 
    } 
} 
+0

此解決方案是線程安全和懶惰的。 http://en.wikipedia.org/wiki/Singleton_pattern#The_solution_of_Bill_Pugh – eSniff 2011-12-22 06:09:46

+0

感謝您的快速回復Ted,您是否知道使用Concurrent包類實現它的任何其他方法? – 2011-12-22 06:10:41

+0

@eSniff - 這不是懶惰:只要加載類,就會創建單例,這可能在調用getInstance()之前就已經完成了。 – 2011-12-22 06:19:47

0

如果10線程使用此Singleton對象在同一時間,然後它會創建操縱每個線程在同一時間修改此對象的問題。爲了避免這種情況,我們使用synchronized關鍵字,因此一次只有一個線程可以訪問Singleton類的對象。如果你希望所有的線程都可以同時訪問這個對象,那麼刪除synchronized關鍵字。

+0

這並不完全正確。唯一的同步是圍繞創建單例的雙重檢查鎖定。在創建單例之後,調用'getInstance()'不需要任何同步。 – 2011-12-22 06:11:08

+0

我可以通過調整類加載器來更改單例的行爲嗎?這個問題在一次採訪中被問到了我。你們有沒有直接面試官試圖指出的想法? – 2011-12-22 06:30:52

8

您是否測試過性能,並得出確實需要延遲初始化的確切結論?如果是這樣,使用持有者模式

public static class Singleton { 
    private static class InstanceHolder { 
     public static Singleton instance = new Singleton(); 
    } 

    private Singleton(){} 

    public static Singleton getInstance() { 
     return InstanceHolder.instance; 
    } 
} 

,如果你以後不俗的性能測試是沒有,比最簡單的事就是初始化單在其實例聲明(渴望初始化),像這樣:

public static class Singleton { 
    public static Singleton instance = new Singleton(); 

    private Singleton(){} 

    public static Singleton getInstance() { 
     return instance; 
    } 
} 

這兩種模式,允許依靠類加載過程,以確保使用Singleton意見一致的情況下任何線程。這樣你就可以獲得兩個好處:代碼更具可讀性,運行速度更快。

順便說一句,Double-Check-Idiom不是線程安全的,除非您的Singleton.instance被宣佈爲volatile

+0

我可以通過調整類加載器來更改Singleton的行爲嗎?這個問題在一次採訪中被問到了我。你們有沒有直接面試官試圖指出的想法? – 2011-12-22 06:31:17

+0

我不確定我是否理解「需求」,但是您可以使用不同的類加載器加載單例,從而在JVM中擁有多個單例實例... – yair 2011-12-22 06:37:55