單例模式(Singleton Pattern)是 Java 中最簡單的設計模式之一。這種型別的設計模式屬於建立型模式,它提供了一種建立物件的最佳方式。
這種模式涉及到一個單一的類,該類負責建立自己的物件,同時確保只有單個物件被建立。這個類提供了一種存取其唯一的物件的方式,可以直接存取,不需要範例化該類的物件。
是否多執行緒安全:是
是否懶載入:否
正如名字含義,餓漢需要直接建立範例。
public class EhSingleton {
private static EhSingleton ehSingleton = new EhSingleton();
private EhSingleton() {}
public static EhSingleton getInstance(){
return ehSingleton;
}
}
缺點: 類載入就初始化,浪費記憶體
優點: 沒有加鎖,執行效率高。還是執行緒安全的範例。
懶漢單例,在類初始化不會建立範例,只有被呼叫時才會建立範例。
是否多執行緒安全:否
是否懶載入: 是
public class LazySingleton {
private static LazySingleton ehSingleton;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (ehSingleton == null) {
ehSingleton = new LazySingleton();
}
return ehSingleton;
}
}
範例在呼叫 getInstance
才會建立範例,這樣的優點是不佔記憶體,在單執行緒模式下,是安全的。但是多執行緒模式下,多個執行緒同時執行 if (ehSingleton == null)
結果都為 true
,會建立多個範例,所以上面的懶漢單例是一個執行緒不安全的範例。
是否多執行緒安全:是
是否懶載入: 是
為了解決多個執行緒同時執行 if (ehSingleton == null)
的問題,getInstance
方法新增同步鎖,這樣就保證了一個執行緒進入了 getInstance
方法,別的執行緒就無法進入該方法,只有執行完畢之後,其他執行緒才能進入該方法,同一時間只有一個執行緒才能進入該方法。
public class LazySingletonSync {
private static LazySingletonSync lazySingletonSync;
private LazySingletonSync() {}
public static synchronized LazySingletonSync getInstance() {
if (lazySingletonSync == null) {
lazySingletonSync =new LazySingletonSync();
}
return lazySingletonSync;
}
}
這樣設定雖然保證了執行緒的安全性,但是效率低,只有在第一次呼叫初始化之後,才需要同步,初始化之後都不需要進行同步。鎖的粒度太大,影響了程式的執行效率。
是否多執行緒安全:是
是否懶載入:是
使用 synchronized
宣告的方法,在多個執行緒存取,比如A執行緒存取時,其他執行緒必須等待A執行緒執行完畢之後才能存取,大大的降低的程式的執行效率。這個時候使用 synchronized
程式碼塊優化執行時間,減少鎖的粒度。
雙重檢驗首先判斷範例是否為空,然後使用 synchronized (LazySingletonDoubleCheck.class)
使用類鎖,鎖住整個類,執行完程式碼塊的程式碼之後,新建了範例,其他程式碼都不走 if (lazySingletonDoubleCheck == null)
裡面,只會在最開始的時候效率變慢。而 synchronized
裡面還需要判斷是因為可能同時有多個執行緒都執行到 synchronized (LazySingletonDoubleCheck.class)
,如果有一個執行緒執行緒新建範例,其他執行緒就能獲取到 lazySingletonDoubleCheck
不為空,就不會再建立範例了。
public class LazySingletonDoubleCheck {
private static LazySingletonDoubleCheck lazySingletonDoubleCheck;
private LazySingletonDoubleCheck() {}
public static LazySingletonDoubleCheck getInstance() {
if (lazySingletonDoubleCheck == null) {
synchronized (LazySingletonDoubleCheck.class) {
if (lazySingletonDoubleCheck == null) {
lazySingletonDoubleCheck = new LazySingletonDoubleCheck();
}
}
}
return lazySingletonDoubleCheck;
}
}
是否多執行緒安全:是
是否懶載入:是
外部類載入時,並不會載入內部類,也就不會執行 new SingletonHolder()
,這屬於懶載入。只有第一次呼叫 getInstance()
方法時才會載入 SingletonHolder
類。而靜態內部類是執行緒安全的。
靜態內部類利用了類載入機制的初始化階段 getInstance()
方法時,虛擬機器器才會載入 SingletonHolder
靜態內部類,
然後在載入靜態內部類,該內部類有靜態變數,JVM會改內部生成
虛擬機器器會保證
這種方式不僅實現延遲載入,也保障執行緒安全。
public class StaticClass {
private StaticClass() {}
private static class SingletonHolder {
private static final SingletonHolder INSTANCE = new SingletonHolder();
}
public static final SingletonHolder getInstance() {
return SingletonHolder.INSTANCE;
}
}
synchronized
宣告同步方法,獲取範例就是安全了。synchronized
宣告方法每次執行緒呼叫方法,其它執行緒只能等待,降低了程式的執行效率。synchronized
程式碼塊,因為只有少量的執行緒獲取範例,範例是null,建立範例之後,後續的執行緒都能獲取到執行緒,也就無需使用鎖了。可能多個執行緒執行到 synchronized
,所以同步程式碼塊還需要再次判斷一次。