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