單例模式概念并不復(fù)雜疲扎,其核心在于這個“單”字絮蒿,全局只有一個,無法創(chuàng)建多余一個的實(shí)例绩卤;單例模式在實(shí)際使用中很常見途样,是一種簡單卻實(shí)用的設(shè)計(jì)模式
使用場景
什么時候需要用到單例模式呢?我認(rèn)為主要有兩種情況
- 最好只有一個實(shí)例濒憋,即某個類在全局中只需要一個實(shí)例即可滿足需求何暇,再創(chuàng)建多余的實(shí)例既無必要,也浪費(fèi)資源凛驮,如一個全局的日志類裆站,或者Spring管理下的Bean默認(rèn)也是單例
- 應(yīng)該只有一個實(shí)例,即某個類的實(shí)例不應(yīng)該超過一個黔夭,否則有可能會產(chǎn)生問題宏胯,如一個數(shù)據(jù)庫連接池,全局就只應(yīng)該只有一個
總體來說纠修,當(dāng)某個類的實(shí)體類最好或應(yīng)該只有一個實(shí)例的時候胳嘲,就應(yīng)該考慮使用單例模式進(jìn)行代碼組織
代碼示例
單例模式比較常用的形式有兩種:饑漢模式與懶漢模式厂僧。這兩種模式的共同點(diǎn)在于都是通過私有化構(gòu)造方法來限制實(shí)例的創(chuàng)建扣草,它們的主要區(qū)別在于實(shí)例是預(yù)加載還是懶加載,一般來說建議使用懶加載的形式颜屠,畢竟有助于節(jié)省資源辰妙,提升效率;另外還有一種創(chuàng)建單例的形式甫窟,大家可能經(jīng)常會用到密浑,但不一定意識到自己其實(shí)寫了個單例,那就是Java的枚舉粗井,首先枚舉沒有課訪問的構(gòu)造器尔破,另外枚舉是通過公有的靜態(tài)final域?yàn)槊總€枚舉常量導(dǎo)出實(shí)例的類街图,這句話是什么意思呢?注意幾個重要的詞:靜態(tài)懒构,final餐济,實(shí)例的類,其實(shí)意思就是說每個枚舉常量都是一個靜態(tài)且final的類的實(shí)例胆剧,我想寫到這里大家應(yīng)該很容易能理解為什么說枚舉也是單例了絮姆,而且是一個final static
的單例
- 饑漢模式(預(yù)加載)
public class EagerSingleton {
private static EagerSingleton instance = new EagerSingleton();
private EagerSingleton(){}
public static EagerSingleton getInstance() {
return instance;
}
}
- 懶漢模式DCL版(懶加載)
public class LazySingleton {
private volatile static LazySingleton instance;
private LazySingleton(){}
public static LazySingleton getInstance() {
if(instance == null){
synchronized(LazySingleton.class){
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
在上述代碼示例的懶漢模式中,使用了DCL(Double checked Locking)秩霍,個人認(rèn)為這其實(shí)是一種不好的形式篙悯,假如不在instance實(shí)例前增加volatile
聲明,那么將有可能會導(dǎo)致錯誤铃绒,即會導(dǎo)致:unsafe publication
鸽照,什么意思呢,即在第一次check時颠悬,即便instance不是空移宅,但是也有可能此時的instance還未初始化完全,數(shù)據(jù)是不完整的椿疗,或錯的漏峰,而使用volatile
在java 5.0之后雖然確實(shí)能避免這樣的問題,但是使得代碼形式顯得比較繁瑣届榄,同時DCL的主要目的在于盡量減少同步帶來的性能損耗浅乔,殊不知這種優(yōu)化基本可以忽略不計(jì),所以我建議一般采用下面簡單的寫法即可
- 懶漢模式簡化版(懶加載)
public class RegularSingleton {
private static RegularSingleton instance;
private RegularSingleton(){}
public synchronized static RegularSingleton getInstance() {
if(instance == null){
instance = new RegularSingleton();
}
return instance;
}
}
一個synchronized
關(guān)鍵字即可铝条,不需要volatile
靖苇,亦不需DCL
- 枚舉型單例
public enum ManKind {
MAN {
@Override
void sad() {
System.out.println("get a sleep");
}
},
WOMAN {
@Override
void sad() {
System.out.println("shopping");
}
};
abstract void sad();
}
應(yīng)用實(shí)例
在JDK源碼中有很多對單例模式的使用,這里列舉幾個大家有興趣可以閱讀一下