“Android設計模式”這個系列主要是對Android專案中的設計模式進行分析總結,學習自《Android 原始碼設計模式解析與實戰》,錯誤之處煩請指正~
Android設計模式系列文章:
一、 概述
1.1 定義
確保某一個類只有一個例項,而且自行例項化並向整個系統提供這個例項。
1.2 使用場景
確保某個類有且只有一個物件的場景,避免產生多個物件消耗過多的資源;或者某種型別的物件應該有且只有一個。
eg:建立一個物件需要消耗的資源過多,如訪問IO和資料庫資源。
1.3 關鍵點
- 建構函式不對外開放,一般為
private
; - 通過一個靜態方法或者列舉返回單例類物件;
- 確保單例類的物件有且只有一個,尤其是在
多執行緒
環境下; - 確保單例類物件在反序列化時不會重新構建物件。
二、實現方式
2.1 懶漢模式
宣告一個靜態物件,並且在使用者第一次呼叫 getInstance
時進行初始化。
2.1.1 分析
-
synchronized
關鍵字用於在多執行緒情況下保證單例物件唯一性 -
優點:單例只有在使用時才會被例項化,在一定程度上節約了資源
-
缺點:
- 每一次載入時需要及時進行例項化,響應速度稍慢
- 每次呼叫
getInstance()
都進行同步,造成不必要的同步開銷
-
一般不建議使用
2.1.2 原始碼
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (null == instance) {
instance = new Singleton();//載入時進行例項化
}
return instance;
}
}
複製程式碼
2.2 餓漢模式
宣告靜態物件時就已經初始化。
2.2.1 分析
-
靜態物件在宣告的時候就已經初始化,從而保證了單例物件唯一性
-
優點: 每次呼叫
getInstance()
直接取出靜態物件,不需要同步鎖,響應速度快 -
缺點:初始化宣告物件造成了一定資源的閒置浪費
2.2.2 原始碼
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
複製程式碼
2.3 Double Check Lock (DCL) 模式
2.3.1 分析
-
優點:
- 資源利用率高
- 既能夠在需要時才初始化單例,又能夠保證執行緒安全,且單例物件初始化後呼叫
getInstance()
不進行同步鎖
-
缺點:
- 第一次載入時響應稍慢
- 由於Java記憶體模型的原因偶爾會失敗
instance = new Singleton();
這句程式碼並不是一個原子操作,由於Java
編譯器允許處理器亂序執行彙編指令以及JDK1.5
之前的JVM (Java Memory Model, Java 記憶體模型)
中Cache、暫存器到主記憶體回寫順序的規定,該語句轉換的彙編指令無法確保順序執行- 在
JDK1.5
之後,具體化了volatile
關鍵字,因此可以直接定義成private volatile static Singleton instance = null;
,就可以保證instance
物件每次都是從主記憶體中讀取
2.3.2 原始碼
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (null == instance) {
synchronized (Singleton.class) {
if (null == instance) {
instance = new Singleton();
}
}
}
return instance;
}
}
複製程式碼
2.4 靜態內部類單例模式
2.4.1 分析
強烈推薦使用
-
優點:
- 第一次載入
Singleton
類時並不會初始化instance
,只有在第一次呼叫getInstance()
時才會初始化 - 既能保證執行緒安全,也能保證單例物件的唯一性,同時也延遲了單例的例項化
- 第一次載入
2.4.2 原始碼
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
/**
* 靜態內部類
*/
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
複製程式碼
2.5 列舉單例
2.5.1 分析
列舉單例模式最大的優點是寫法簡單,列舉在 Java
中與普通類是一樣的,不僅能夠有欄位,還能夠有自己的方法。最重要的是預設列舉例項的建立時執行緒安全的,並且在任何情況下它都是一個單例。
在上述的幾種單例模式中,反序列化 的時候會出現重新建立物件的情況。
上述示例中如果要杜絕單利物件在被反序列化時重新生成物件,則必須加入如下方法:
private Object readResolve() throws ObjectStreamException {
return instance;
}
複製程式碼
2.5.2 原始碼
public enum Singleton {
INSTANCE;
public void doSomething() {
// ... do something
}
}
複製程式碼
2.6 使用容器實現單例模式
2.6.1 分析
在程式初始化的時候,將多種單例型別注入到一個統一的管理類中,在使用時根據 key
獲取物件對應型別的物件。
這種方式使得我們可以管理多種型別的單例,並且在使用時候可以通過統一的介面進行獲取操作,降低了使用者的使用成本,也對使用者隱藏了具體實現,降低了耦合度。
2.6.2 原始碼
public class SingletonManager {
private static Map<String, Object> data = new HashMap<>();
public SingletonManager() {
}
public static void register(String key, Object instance) {
if (!data.containsKey(key)) {
data.put(key, instance);
}
}
public static Object get(String key) {
return data.get(key);
}
}
複製程式碼
三、小結
所有的單例模式核心原理都是將建構函式私有化,並且通過靜態方法獲取一個唯一的例項。
需要注意的是在獲取例項的過程中保證執行緒安全、防止反序列化導致重新生成例項物件等問題。
具體選擇哪種方式實現單例模式還需要結合專案業務邏輯。
本文對 單例模式 的分析到此就結束了,部分內容學習自 《Android原始碼設計模式 解析與實戰》。