1. 程式人生 > >Android的單例模式的N種實現方式

Android的單例模式的N種實現方式

推薦書籍:《Android原始碼設計模式 第二版》

單例模式的定義及使用場景

定義:確保某個類只有一個例項,而且自行例項化提供給外部使用。

使用場景:某個型別的物件只應該有且只有一個,或者避免建立多個物件消耗過多的資源時。如:訪問IO或資料庫時要考慮單例模式。

N種實現方式及比較 

餓漢式

public class SingleTon {
    //將建構函式私有化
    private SingleTon() {
    }
 
    //建立私有例項物件
    private static final SingleTon singleTonInstance = new SingleTon();
 
    //對外提供方法,返回例項物件
    public static SingleTon getInstance() {
        return singleTonInstance;
    }
}

優點:簡單,執行緒安全。

缺點:例項物件是static的,在宣告的時候就例項化了,浪費資源。

懶漢式

public class SingleTon {
    //宣告私有化
    private static SingleTon singleTonInstance;
    //將建構函式私有化
    private SingleTon() {
    }
    //懶漢式
    private static synchronized SingleTon getInstance(){
        if (null==singleTonInstance){
            singleTonInstance = new SingleTon();
        }
        return singleTonInstance;
    }
}


優點:用到的時候才會去例項化,在一定程度上節約了資源。

缺點:getInstance方法是用synchronized修飾的,該方法是同步的,為了保證執行緒安全,但是導致每次呼叫該方法的時候都會被同步,這樣會消耗不必要的資源(不必要的同步開銷)。所以這種模式一般不建議使用。

Double Check Lock(DCL模式):雙重檢查鎖定

public class SingleTon {
    //宣告私有化
    private static SingleTon singleTonInstance;
    //將建構函式私有化
    private SingleTon() {
    }
    //Double Check Lock
    public static SingleTon getInstance(){
        if (singleTonInstance==null){
            synchronized (SingleTon.class){
                if (singleTonInstance==null){
                    singleTonInstance = new SingleTon();
                }
            }
        }
        return singleTonInstance;
    }
}


可以看到getInstance()方法對singleTonInstance進行兩次判空,對懶漢式進行了優化,只有在第一次例項化的時候才會走第二個分支,才會同步,避免了每次都同步造成的不必要的資源消耗。

優點:第一次執行getInstance方法時才會例項化,資源利用率高,效率高。

缺點:偶爾失效(高併發條件下,由於JDK版本問題,在jdk1.5之前會失敗)

靜態內部類實現

public class SingleTon {
    //將建構函式私有化
    private SingleTon() {
    }
 
    public static SingleTon getInstance() {
        return SingleTonHoulder.singleTonInstance;
    }
 
    //靜態內部類
    public static class SingleTonHoulder {
        private static final SingleTon singleTonInstance = new SingleTon();
    }
}

第一次呼叫getInstance()方法的時候,虛擬機器才會載入SingleTonHoulder靜態內部類

優點:執行緒安全,保證單例的唯一性,延遲了物件的例項化,是推薦的方式。

缺點:

列舉

以上的單例實現方法都沒有考慮一個因素:反序列化,即使建構函式是私有的,反序列化仍然有特殊的途徑去建立類的一個新的例項。但是同構列舉實現單例不會有這樣的問題,因為列舉提供了序列化機制。

public enum SingleTon {
    INSTANCE;
    
}

具體這點我還不是太理解。以後理解了再分享吧。

使用容器實現單例

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


將多種單例型別注入到一個統一的管理類中,使用時根據key獲取對應的物件,這種模式使得我們可以和管理多種型別的單例,並且在使用的時候可以通過統一的介面進行獲取操作,降低了使用者的使用成本,也對使用者隱藏的具體的實現,降低了耦合度。

Android原始碼中單例使用

LayoutInflater.from(Context context);

EventBus.getDefault()

ImageLoader.getInstance();

......

他們實現單例的方式不同,大家可以去看原始碼

總結

單例模式是使用頻率較高的設計模式,但是由於客戶端通常沒有高併發的情款,選擇哪種實現方式並不會有太大影響。但是出於效率考慮,推薦使用“DCL”和“靜態內部類”實現方式。

單例模式的優點:

1.在記憶體中只有一個例項,減少記憶體開支

2.只生產一個例項,減少系統性能的效能開銷

3.避免對資源的多重佔用。

4.可以在系統設定全域性的訪問點,優化和共享資源訪問。(例如可以設定一個單例類,負責所有資料表的對映處理)

單例的缺點;

1.單例一般沒有介面,擴充套件很困難。

2.單例如果持有Context物件,很容易引起記憶體洩漏,最好傳遞全域性的Application Context。


原文:https://blog.csdn.net/anyanyan07/article/details/72039601