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