什么是單例模式记某?
單例模式,是一種常用的軟件設(shè)計(jì)模式构捡,屬于對(duì)象創(chuàng)建模式的一種液南。在應(yīng)用這個(gè)模式時(shí),單例對(duì)象的類(lèi)必須保證只有一個(gè)實(shí)例存在勾徽。
單例模式的使用場(chǎng)景滑凉?
- 系統(tǒng)關(guān)鍵組件,需要保證系統(tǒng)中只有一個(gè)實(shí)例;
- 類(lèi)的初始化需要消耗較多的系統(tǒng)資源畅姊;
如何實(shí)現(xiàn)咒钟?
- 私有的構(gòu)造函數(shù)。杜絕外部通過(guò)構(gòu)造方法顯式創(chuàng)建類(lèi)的實(shí)例若未,確保系統(tǒng)中僅有一個(gè)實(shí)例的存在朱嘴;
- 提供單例實(shí)例的全局訪(fǎng)問(wèn)點(diǎn)。一般為
Method name
為getInstance()
的靜態(tài)方法粗合,返回結(jié)果為單例實(shí)例萍嬉; - 獲取單例實(shí)例時(shí),首先判斷虛擬機(jī)中實(shí)例是否已存在隙疚,如果有則返回壤追,如果沒(méi)有則創(chuàng)建;
常見(jiàn)寫(xiě)法及分析
單例模式雖然簡(jiǎn)單供屉,實(shí)現(xiàn)方式卻比孔乙己老先生的茴字寫(xiě)法還要多行冰。以下:
餓漢模式
/**
* 單例(饑餓模式):靜態(tài)變量初始化保證線(xiàn)程安全
*/
public final class HungerSingleton {
/**
* 私有構(gòu)造方法,保證不能通過(guò) new 創(chuàng)建新的實(shí)例
*/
private HungerSingleton(){}
/**
* 靜態(tài)示例變量
*/
private static final HungerSingleton INSTANCE = new HungerSingleton();
/**
* 獲取類(lèi)的單例實(shí)例
*
* @return
*/
public static HungerSingleton getInstance(){
return INSTANCE;
}
}
餓漢模式是最常用的單例寫(xiě)法贯卦,JDK 中的
java.lang.Runtime
就是這樣實(shí)現(xiàn)的资柔,基于classloader
機(jī)制,這種寫(xiě)法可以避免多線(xiàn)程的同步問(wèn)題撵割;不過(guò)instance
在類(lèi)裝載的時(shí)候就實(shí)例化贿堰,容易產(chǎn)生垃圾對(duì)象,對(duì)于基礎(chǔ)服務(wù)中的非核心模塊啡彬,不建議使用餓漢模式羹与,而應(yīng)該使用懶加載的單例寫(xiě)法-“按需創(chuàng)建”。
Synchronized 版(懶漢式)
/**
* 同步版本的單例
* 1 懶加載
* 2 使用 Synchronized 關(guān)鍵字來(lái)實(shí)現(xiàn)互斥
* 3 最常見(jiàn)的懶漢版本
*/
public final class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {
if (instance != null) {
throw new IllegalStateException("單例實(shí)例已經(jīng)初始化");
}
}
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
同步版本的單例實(shí)現(xiàn)了懶加載庶灿,在多線(xiàn)程環(huán)境中也可以很好的工作纵搁,但是,效率很低往踢,因?yàn)槎鄶?shù)情況下都不需要同步剥汤。雙重檢查鎖版本是對(duì)
Synchronized
版本的改進(jìn)气堕。
雙重檢查鎖版(懶漢式)
/**
* 雙重檢查鎖單例
* 1 懶加載铆惑;
* 2 線(xiàn)程安全(通過(guò)鎖機(jī)制來(lái)保證)
* 相比 Synchronized 的寫(xiě)法會(huì)有性能上的提升掏颊,主要體現(xiàn)在不會(huì)每次獲取實(shí)例都獲取鎖(只有在實(shí)例沒(méi)有初始化的情況下,才會(huì)獲取鎖)瘦癌;
*/
public final class DoubleCheckLockingSingleton {
private static volatile DoubleCheckLockingSingleton instance;
/**
* 私有構(gòu)造方法
*/
private DoubleCheckLockingSingleton(){
// 判斷單例是否已經(jīng)初始化猪贪,來(lái)組織反射創(chuàng)建單例
if (instance != null) {
throw new IllegalStateException("單例對(duì)象已經(jīng)初始化");
}
}
/**
* 單例方法
*
* @return
*/
public static DoubleCheckLockingSingleton getInstance(){
// 局部變量提升 25% 的性能?存疑 TODO 待考證
DoubleCheckLockingSingleton result = instance;
if (result == null) {
synchronized (DoubleCheckLockingSingleton.class) {
result = instance;
if (result == null) {
instance = result = new DoubleCheckLockingSingleton();
}
}
}
return result;
}
}
這種方式是懶加載常用的寫(xiě)法讯私,在多線(xiàn)程環(huán)境中也可以保證單例的正確創(chuàng)建热押,
getInstance
方法執(zhí)行效率比Synchronized
版本要高西傀。
靜態(tài)內(nèi)部類(lèi)版(懶漢式)
/**
* 靜態(tài)內(nèi)部類(lèi)延遲加載單例
*/
public final class OnDemandHolderSingleton {
/**
* 私有構(gòu)造方法
*/
private OnDemandHolderSingleton(){}
/**
* 獲取單例實(shí)例
*
* @return
*/
public static OnDemandHolderSingleton getInstance() {
return SingletonHolder.instance;
}
/**
* 靜態(tài)內(nèi)部類(lèi)
*/
private static class SingletonHolder {
private static final OnDemandHolderSingleton instance = new OnDemandHolderSingleton();
}
}
《Java并發(fā)編程實(shí)踐》中推薦的單例寫(xiě)法,實(shí)現(xiàn)相比雙重檢查鎖簡(jiǎn)單桶癣。適用于對(duì)靜態(tài)域的延遲初始化拥褂;這種方式同樣利用了
classLoader
機(jī)制來(lái)保證instance
初始化時(shí)的線(xiàn)程安全。內(nèi)部類(lèi)SingletonHolder
只有在OnDemandHolderSingleton.getInstance()
方法調(diào)用時(shí)牙寞,才會(huì)執(zhí)行類(lèi)加載肿仑,進(jìn)而觸發(fā)instance
的創(chuàng)建。
枚舉
/**
* 枚舉 : 枚舉也是一種單例碎税。
*/
public enum EnumSingleton {
INSTANCE;
}
天生單例;不過(guò)這種寫(xiě)法很少見(jiàn)馏锡;
參考鏈接
碼云項(xiàng)目鏈接:https://gitee.com/maolv/java_code_example/tree/master/design_pattern/src/main/java/com/gitee/maolv/java/pattern/singleton