手撕單例的五種寫法!
單例模式是一種常見的設計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取該實例。當然,它也是面試中的常客,尤其是某手面試中經常要求應聘者手撕,所以今天咱們就來盤盤它。
單例模式的實現方式有很多,如下圖所示:
具體實現如下。
1.餓漢式模式
此在餓漢式單例模式中,實例在類加載時就被創建,這種方式的優點是實現簡單,線程安全(因為類加載過程是線程安全的)。缺點是可能會導致實例過早創建,如果實例創建過程比較耗時或者占用大量資源,而在程序運行初期并不需要該實例,就會造成資源浪費。
public class Singleton {
// 1.私有靜態成員變量,在類加載時就創建實例
private static Singleton instance = new Singleton();
// 2.私有構造函數,防止外部通過構造函數創建實例
private Singleton() {}
// 3.公共靜態方法,用于獲取唯一的實例
public static Singleton getInstance() {
return instance;
}
}
2.懶漢模式(非安全)
懶漢式單例模式在第一次調用 getInstance 方法時才創建實例,這樣可以避免實例過早創建。但上述代碼是非線程安全的,在多線程環境下,可能會出現多個線程同時進入 if 語句,導致創建多個實例的情況。
public class Singleton {
// 1.私有靜態成員變量,初始化為null
private static Singleton instance = null;
// 2.私有構造函數,防止外部通過構造函數創建實例
private Singleton() {}
// 3.公共靜態方法,用于獲取唯一的實例
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
3.懶漢模式(安全效率低)
此版本的懶漢式單例模式通過在 getInstance 方法上添加 synchronized 關鍵字,使其成為線程安全的。但這種方式的缺點是每次調用 getInstance 時都需要獲取鎖,會導致性能下降,尤其是在高并發環境下。
public class Singleton {
// 1.私有靜態成員變量,初始化為null
private static Singleton instance = null;
// 2.私有構造函數,防止外部通過構造函數創建實例
private Singleton() {}
// 3.公共靜態方法,用于獲取唯一的實例
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
4.雙重檢查鎖模式
雙重檢查鎖定模式在懶漢式基礎上進行了優化,通過兩次檢查 instance 是否為 null,既保證了在第一次需要實例時創建實例,又在一定程度上避免了每次調用 getInstance 都獲取鎖的情況,提高了性能。不過,由于指令重排序等問題,可能會導致一些錯誤,因此需要在 instance 變量前添加 volatile 關鍵字來解決。
public class Singleton {
// 1.私有靜態成員變量,初始化為null
private volatile static Singleton instance = null;
// 2.私有構造函數,防止外部通過構造函數創建實例
private Singleton() {}
// 3.公共靜態方法,用于獲取唯一的實例
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
5.靜態內部類模式
這種方式利用了靜態內部類的特性,當外部類被加載時,靜態內部類不會被加載,只有當調用 getInstance 方法時,靜態內部類才會被加載,此時才創建單例實例。這種實現方式既保證了線程安全,又避免了在不需要實例時過早創建實例,是一種比較常用的單例模式實現方式。
public class Singleton {
// 1.私有構造函數,防止外部通過構造函數創建實例
private Singleton() {}
// 2.靜態內部類,其中包含單例實例
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
// 3.公共靜態方法,用于獲取唯一的實例
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
小結
單例模式雖然實現方式有 5 種:餓漢模式、懶漢非安全模式、懶漢安全模式、雙重較驗鎖模式、靜態內部類模式,但它的寫法基本都是以下三步:
- 定義私有構造方法(防止 new 多個實例)。
- 定義私有變量(承接單例對象)。
- 定義統一返回對象的方法。