Talk is cheap, show me the code.
常用多種單列模式钱反,如下:
public class SingleExample {
? ? public static SingleExample singleInstance;
? ? private SingleExample(){}
? ? public SingleExample getInstance(){
? ? ? ? if(singleInstance==null)singleInstance=new SingleExample();
? ? ? ? return singleInstance;
? ? }
}
僅有一個線程引用該形式髓窜,是沒有問題的华坦,如果多線程使用時拳昌,無法保證僅創(chuàng)建一個實例常摧;
如下進行一次優(yōu)化
public synchronized SingleExample getInstance(){
? ? if(singleInstance==null)singleInstance=new SingleExample();
? ? return singleInstance;
}
使用線程鎖零抬,保證單例唯一性灾螃;但是如此就會每次調(diào)用getInstance需要進行同步题翻,浪費不需要的資源;
public synchronized SingleExample getInstance(){
? ? if(singleInstance==null){
? ? ? ? synchronized (SingleExample.class){
? ? ? ? ? ? if(singleInstance==null){
? ? ? ? ? ? ? ? singleInstance=new SingleExample();
? ? ? ? ? ? }
}
}
? ? return singleInstance;
}
雙重校驗(DCL)會在某些情況失效,原因如下:
? singleInstance=new SingleExample();這個執(zhí)行步驟如下:
1)給singleInstance分配內(nèi)存腰鬼;
2)調(diào)用SingleExample()構造函數(shù)嵌赠,初始化成員變量;
3)將singleInstance對象指向分配的內(nèi)存空間熄赡;(此時singleInstance就不是null了)
????????但是姜挺,由于java編譯器允許處理器亂序執(zhí)行,以及JDK1.5前JMM(Java內(nèi)存模型)中Cache彼硫,寄存器到主內(nèi)存回寫順序的規(guī)定炊豪,上面的第二和第三的順序是無法保證的,也就導致上面步驟的執(zhí)行順序可能是1-2-3拧篮,也可能是1-3-2词渤。
? ? ? ? 這樣就導致在A線程執(zhí)行完1-3后,B線程判斷singleInstance不為null串绩,直接使用singleInstance缺虐,導致程序異常。
? ? ? ? 在JDK1.5后礁凡,SUN官方優(yōu)化了volatile關鍵字去解決該問題高氮,保證singleInstance不為null時慧妄,已經(jīng)進行了初始化;
? ???????public volatile?static SingleExample singleInstance;
當然使用volatile關鍵字會對內(nèi)存有寫影響剪芍,但是可以保證程序執(zhí)行的正確性塞淹。
但是如上的方式是比較繁瑣,對性能也是有些影響的罪裹,我們可以通過Java中類的靜態(tài)成員變量初始化機制(當且僅在類被加載時饱普,進行初始化)來實現(xiàn)單例模式,如下坊谁;
public class SingleExample {
? ? private SingleExample(){}
? ? public SingleExample getInstance(){
? ? ? ? return SingletonHolder.singleInstance;
? ? }
? ? private static class? SingletonHolder{
? ? ? ? //靜態(tài)成員變量僅在類初次加載時费彼,進行初始化,這樣保證了對象唯一性
? ? ? ? private static final SingleExample singleInstance=new SingleExample();
? ? }
}