單例模式是設(shè)計(jì)模式中最常用到的一種模式,一般應(yīng)用于定位管理、線程管理、文件管理、網(wǎng)絡(luò)管理等類(lèi)上面,讓這些類(lèi)的單一實(shí)例來(lái)處理App的各個(gè)模塊邏輯等。
這里我就不多舉例子了,相信大家應(yīng)該都會(huì)用,我就將基本所有類(lèi)型的單例實(shí)現(xiàn)方式列舉出來(lái),供大家參考吧。
(1)幾種常見(jiàn)的單例模式模板
- 餓漢單例
“餓漢”,指的是代碼很早就想要初始化實(shí)例出來(lái),于是,此形式的類(lèi)實(shí)例在類(lèi)編譯加載在內(nèi)存中的時(shí)候就初始化創(chuàng)建完畢。
public class Singleton{
private static Singleton sInstance = new SingleTon();
private Singleton() {}
public Singleton getInstance(){
return sInstance;
}
}
優(yōu)點(diǎn):類(lèi)加載時(shí)實(shí)例化對(duì)象,避免了多線程同步創(chuàng)建問(wèn)題。
缺點(diǎn):類(lèi)加載時(shí)就進(jìn)行初始化,即還沒(méi)用到它,它就占了App進(jìn)程的部分內(nèi)存,造成一定的內(nèi)存浪費(fèi)。【沒(méi)有懶加載】
- 懶漢單例(加了
synchronized
,保證線程安全)
“懶漢”,相對(duì)于“餓漢”來(lái)說(shuō),就不是在編譯時(shí)急著初始化了,而是在調(diào)用到類(lèi)靜態(tài)方法要用到一個(gè)實(shí)例時(shí),他再初始化單例對(duì)象。
public class LazySingleTon {
private static LazySingleTon sInstance;
private LazySingleTon(){
}
public static synchronized LazySingleTon getInstance(){
if(sInstance==null){
sInstance = new LazySingleTon();
}
return sInstance;
}
}
注意,以上的code,方法加synchronized
與否,直接影響讀取性能。
- DCL 單例(雙重檢查鎖定)
public class DCLSingleTon {
// private static DCLSingleTon sInstance;
private volatile static DCLSingleTon sInstance = null;
///考慮到 DCL失效問(wèn)題:JDK1.5之前的JMM(Java內(nèi)存模型)中的Cache、寄存器到主內(nèi)存回寫(xiě)順序的規(guī)定,上面三件事中的后面兩件事的順序無(wú)法保證。
///這樣,如果是執(zhí)行1->3->2的執(zhí)行順序,那么,在內(nèi)部成員都沒(méi)有被初始化的情況下,sInstance就已經(jīng)被賦值為非null了,那就后面會(huì)產(chǎn)生錯(cuò)誤了。
///于是,>=JDK1.5時(shí),可以這樣:
// private volatile static DCLSingleTon sInstance = null;
///雖然這樣,加載時(shí)會(huì)影響性能,但是,還是值得的。
///這樣一來(lái),就可以每次在主內(nèi)存中讀取對(duì)象了。
private DCLSingleTon(){
}
public static DCLSingleTon getInstance(){
if(sInstance== null){
synchronized (DCLSingleTon.class) {
if(sInstance == null){
sInstance = new DCLSingleTon();
}
}
}
return sInstance;
}
}
- 優(yōu)點(diǎn):既能夠在需要時(shí)才初始化單例【懶加載】,又能夠保證線程安全,而且,單例對(duì)象初始化后的getInstance調(diào)用是不會(huì)進(jìn)行同步鎖的【第一次要初始化對(duì)象所以慢,第二次之后就很快】。
- 缺點(diǎn):加入了
volatile
關(guān)鍵字(JDK1.5以上),保證Java編譯器執(zhí)行順序,但影響了性能?!静贿^(guò),這個(gè)影響很小】
- 靜態(tài)內(nèi)部類(lèi)單例【推薦】
public class StaticSingleTon {
private static StaticSingleTon sInstance;
private StaticSingleTon(){}
public static StaticSingleTon getInstance(){
return StaticSingleTon.sInstance;
}
/// 靜態(tài)內(nèi)部類(lèi)
private static class SingleTonHolder{
private static final StaticSingleTon sInstance = new StaticSingleTon();
}
}
- 特點(diǎn):同樣 是 只有第一次調(diào)用
getInstance()
時(shí),才會(huì)加載SingleTonHolder
類(lèi),也才會(huì)初始化對(duì)象。 - 解決了DCL亂序問(wèn)題【不怕他會(huì)執(zhí)行亂序。一定是先初始化對(duì)象,然后獲取對(duì)象。】
- 枚舉單例
public enum EnumSingleTon {
DOG,CAT;
public void bark(){
System.out.println(toString()+"吠了一聲!");
}
}
然后,我們可以直接調(diào)用:
DOG.bark();
DOG.bark();
CAT.bark();
CAT.bark();
永遠(yuǎn)只存在一條狗和一只貓的實(shí)例。
- 優(yōu)點(diǎn):解決了對(duì)象反序列化問(wèn)題(任何時(shí)候都是單例)
- 什么是“反序列化問(wèn)題”? 答:就是,即使你的單例類(lèi)的構(gòu)造器是
private
的,但是到反序列化那一步的時(shí)候,依然會(huì)通過(guò)特殊手段去調(diào)用該方法,來(lái)實(shí)例化一個(gè)新的實(shí)例?!緎o,將對(duì)象寫(xiě)入磁盤(pán),再讀取出來(lái)的過(guò)程,就會(huì)新建對(duì)象,而不是用回原來(lái)的實(shí)例?!?/li>
- 使用容器
使用容器,同樣能實(shí)現(xiàn)單例模式。
public class SingleTonManager {
private static Map<String, Object> objMap = new HashMap<String,Object>();
private SingleTonManager(){}
public static void registerService(String key , Object instance){
if(! objMap.containsKey(key)){
objMap.put(key, instance);
}
}
public static Object getInstance(String key){
return objMap.get(key);
}
}