java單例設計模式

1.應用場景:

當需要保證類在內存中的對象唯一性,可以使用單例模式,
不想創建多個實例浪費資源,或者避免多個實例由于多次調用
而出現錯誤。一般寫工具類,線程池,緩存,數據庫等可以用到。

2.各種各樣的單例寫法

1.餓漢模式

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

    private Singleton() {
    }

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

獲取單例對象:Singleton instace = Singleton.getInstance();
優點:類加載的時候就完成實例化,避免線程同步問題
缺點:由于在類加載的時候就進行了實例化,所以沒有達到Lazy Loading(懶加載)
的效果,即使我們沒用到這個實例,但是他還是會加載,從而造成內存浪費(可以忽略,
最常用的一種)。

2.懶漢模式(線程不安全)

public class Singleton {
    
    private static Singleton instance = null;

    private Singleton() {
    }

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

盡管達到了懶加載,但是卻存在線程安全問題,比如有兩個線程,剛好都
執行完if(instance == null),接著準備執行instance = new Singleton()
語句,這樣的結果會導致我們實例化了兩個Singleton對象,這就是懶漢單例
模式可能會引發的線程安全問題,解決這個方法,我們可以對getInstance方法加鎖。

3.懶漢模式(線程安全,但效率低)

public class Singleton {
    
    private static Singleton instance = null;

    private Singleton() {
    }

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

盡管保證了線程安全,但是每個線程在想要獲得實例時,執行getInstance()
方法都需要進行同步,而實例化代碼只需執行一次就夠了,后面獲取該實例,
直接return即可,方法進行同步效率太低,需要改進。還有一種寫法是:
synchronized (Singleton.class) { instance = new Singleton(); }
這樣一樣是線程不安全的,如果你想使用懶漢模式的話,推薦使用下面的:
DCL單例(雙重檢查鎖定)。

4.懶漢模式雙重校驗鎖

public class Singleton {
    
    private static volatile Singleton instance = null;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if(null == instance){
            synchronized (Singleton.class) {
                if(null == instance){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

代碼中進行了兩次if檢查,這樣就可以保證線程安全,初始化一次后,
后面再次訪問時,if檢查,直接return 實例化對象。volatile是1.5后
引入的,volatile關鍵字會屏蔽Java虛擬機所做的一些代碼優化,會導
致系統運行效率降低,而更好的寫法是使用靜態內部類來實現單例!

5.靜態內部類實現單例模式

public class Singleton {

    private Singleton() {
    }

    public static final Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

和餓漢式類似,兩者都是通過類裝載機制來保證初始化實例
的時候只有一個線程,從而避免線程安全問題,餓漢式的
Singleton類被加載,就會實例化,而靜態內部類這種,
當Singleton類被加載時,不會立即實例化,調用getInstance方法,
才會裝載SingletonHolder類,從而完成Singleton的實例化。

6.枚舉實現單例模式

public enum SingletonEnum {
    INSTANCE;
    private Singleton instance;
    SingletonEnum() { 
        instance = new Singleton()
    }
    public Singleton getInstance() {
        return instance;
    }
}

訪問方式:SingletonEnum.INSTANCE.method();
INSTANCE即為SingletonEnum類型的引用,得到它就可以調用
枚舉中的方法。既避免了線程安全問題,還能防止反序列化
重新創建新的對象,但是失去了類的一些特性,沒有延時加載,
推薦使用。

7.使用容器實現單例模式

public class SingletonManager {
    private static Map<String,Object> objMap = new HashMap<String,Object>();
    private Singleton() { }
    public static void registerService(String key,Object instance) {
        if(!objMap.containsKey(key)) {
            objMap.put(key,instance);
        }
    }
    public static Object getService(String key) {
        return objMap.get(key);
    }
}

將多種單例類型注入到一個統一的管理類中,在使用時根據key獲取對象
對應類型的對象。這種方式使得我們可以管理多種類型的單例,并且在使
用時可以通過統一的接口進行獲取操作,降低了用戶的使用成本,也對用
戶隱藏了具體實現,降低了耦合度。


優缺點

優點:保持類對象唯一性,對于頻繁創建和銷毀的對象可以提高性能。
缺點:擴展困難,單例的方法無法生成子類對象,要擴展的話基本要重寫這個類。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容