源碼地址
說明:
此筆記是在看完
Android 源碼設計模式解析與實戰
中單例模式進行的總結。
使用場景
確保某個類有且只有一個對象,避免產生多個對象消耗過多的資源。
例如:創建一個對象需要消耗的資源過多,如IO、數據庫操作等。
實現關鍵點
- 私有化構造函數;
- 通過一個靜態方法或者枚舉返回單例類對象;
- 確保單例類對象有且只有一個,尤其在多線程環境下;
- 確保單例類對象在反序列化時不會重新構建對象。
6種創建模式
-
餓漢模式
特點:
- 單例類加載的時候就已經初始化;
- 由于在類加載的時候就創建實例對象,比較消耗資源,但是第一次加載時較快。
示例:
public class Singleton { private static Singleton sInstance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return sInstance; } }
-
懶漢模式
特點:
- 只有在使用時才初始化實例,在一定程度上節約了資源;
- 第一次加載時需要進行實例化,反應稍慢;
- 每次調用 getInstance 都進行同步,造成不必要的開銷。
示例:
public class Singleton { private static Singleton sInstance; private Singleton() { } public static synchronized Singleton getInstance() { if(sInstance == null) { sInstance = new Singleton(); } return sInstance; } }
-
Double Check Lock (DCL)模式
特點:
- 資源利用率高,第一次執行 getInstance 時單例對象才會被實例化,效率高,并且線程安全;
- 第一次加載反應稍慢,在高并發狀態下有一定的缺陷。可以使用關鍵字 volatile 進行優化。如:private volatile static Singleton instance;
示例:
public class Singleton { private static Singleton sInstance; private Singleton() { } public static Singleton getInstance() { if(sInstance == null) { synchronized (Singleton.class) { if (sInstance == null) { sInstance = new Singleton(); } } } return sInstance; } }
-
靜態內部類單例
特點:
- 只有在第一次調用 Singleton 的 getInstance 方法時才會實例化;
- 線程安全,可以確保單例的唯一性。
示例:
public class Singleton { private Singleton() { } public static Singleton getInstance() { return SingletonHolder.sInstance; } /** 靜態內部類 */ private static class SingletonHolder { private static Singleton sInstance = new Singleton(); } }
-
枚舉單例
特點:
寫法簡單,線程安全;
上面的幾種寫法,反序列化會出現重新創建對象的情況,而枚舉不會;
-
如果需要杜絕這個問題,需要在類中創建一個方法,寫法如下:
private Object readRessolve() throws ObjectStreamException { return sInstance; }
示例:
public enum SingletonEnum { INSTANCE; public void doSomething() { //doSomething... } }
?
-
使用容器實現單例
特點:
- 在程序的初始,將多種單例類型注入到一個統一的管理類中,使用時根據 key 獲取對應類型的對象;
- 方便管理多種類型的單例。
示例:
public class SingletonManager { private static Map<String, Object> objMap = new HashMap<>(); private SingletonManager() { } 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); } }
Android 源碼中的單例
LayoutInflater
詳情參見
Android 源碼設計模式解析與實戰
。
總結
- 優點
- 由于單例模式中內存只有一個實例,減少了內存開支,特別對于一個對象需要頻繁地創建、銷毀時,優勢比較明顯;
- 當創建一個對象需要較多的資源時,可以使用單例解決;
- 單例模式可以避免對資源的多重占用。例如一個寫文件操作,由于只有一個實例存在內存中,避免對同一個資源文件的同時寫操作。
- 單例模式可以在系統設置全局訪問點,優化和共享資源訪問。例如,可以設計一個單例類,負責所有數據表的映射處理。
- 缺點
- 單例模式一般沒有借口,擴展很困難。若要擴展,需要在源代碼上進行修改。
- 單例對象如果持有 Context ,那么很容易引發內存泄漏,此時需要注意傳遞給單例對象 Application Context。