設計模式系列-----------單例模式

單例模式:確保一個類只有一個實例,並提供一個全局訪問點。

什麼情況下需要單例模式?

      一些類提供公共功能供別人調用,本身不會處理業務邏輯
      類會被許多類和線程調用
單例模式的一些注意點:
     單例的生存期超長,會導致內存的持續佔用。
     單例在多線程環境需要小心的處理線程互斥,進行資源保護。
     單例在類的繼承樹中不利於使用,會破壞繼承體系。
     單例的全局可見性帶來的設計破壞。

經典代碼示例以及改進:

public class Singleton {
    private static Singleton uniqueInstance ;

    public static Singleton getInstance() {
        if (uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

    private Singleton() {
    }
}
上面的代碼實現了延遲加載(假如一個單例類需要耗費好多資源時候延遲加載就有很大好處)但是這種做法在多線程的時候會出現問題,比如有兩個線程同時調用getInstance(),這時會new兩個對象。

把getInstance()方法變成同步(synchronized)方法:

public class Singleton {
    private static Singleton uniqueInstance ;

    public static synchronized Singleton getInstance() {
        if (uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }

    private Singleton() {
    }
}
但是同步會降低性能。

綜合考慮有以下幾種解決方案:

1、如果getInstance()的性能對程序不是很關鍵,那就不用考慮太多。

2、不用延遲實例化的方法。

public class Singleton {
    private static Singleton uniqueInstance = new Singleton();//在靜態初始化器中創建單例,這段代碼保證了線程安全

    public static Singleton getInstance() {

        return uniqueInstance;
    }

    private Singleton() {
    }
}
3、雙重檢查加鎖,在getInstance()中減少使用同步。

public class Singleton {
    private volatile static Singleton uniqueInstance ;

    public static Singleton getInstance() {
        if (uniqueInstance == null){//A
            synchronized (Singleton.class){
                if (uniqueInstance == null){//C
                    uniqueInstance = new Singleton();//B
                }
            }
        }

        return uniqueInstance;
    }

    private Singleton() {
    }
}
爲什麼需要兩次判斷爲null?
其實這個意義在於防止多個線程同時進入第一個if內,比如說線程A執行到A行,線程B執行到B行,線程B還沒有返回。當線程A執行到C行,這時線程B初始化實例完畢,如果沒有裏面的再一次判斷就會生成兩個實例!

其他需要注意點事項:

1、單例模式和靜態類(把所有的方法和變量都定義成靜態的)的區別

2、如果程序中存在多個類加載器,則不同的類加載器有可能加載同一個類,這種情況下就會出現多個單例類實例並存。這種情況下則需要自行指定類加載器,並指定同一個類加載器。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章