一、單例模式的陷阱
設(shè)計(jì)模式中的單例模式應(yīng)該是被大家使用最廣泛的模式之一,但網(wǎng)上關(guān)于單例模式的詬病也不少,最集中的就是:在多線程的環(huán)境下,單例模式有可能返回不止一個(gè)的對(duì)象。那么到底為什么會(huì)出現(xiàn)這種情況呢?下面我們來(lái)看單例模式的兩種實(shí)現(xiàn)方式
方式一
public class Singleton {
//注意構(gòu)造方法必須是私有的
private Singleton(){}
//在自己內(nèi)部定義自己一個(gè)實(shí)例,是不是很奇怪?
//注意這是private 只供內(nèi)部調(diào)用
private static Singleton instance = new Singleton();
//這里提供了一個(gè)供外部訪問(wèn)本class的靜態(tài)方法,可以直接訪問(wèn)
public static Singleton getInstance() {
return instance;
}
}
方式二
public class Singleton {
private static Singleton instance = null;
public static synchronized Singleton getInstance() {
//這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次
//使用時(shí)生成實(shí)例!
if (instance==null)
instance=new Singleton();
return instance;
}
}
}
方式二就是我們說(shuō)的:滯后初始化(Lazy Initialization)。為什么會(huì)有滯后初始化這種實(shí)現(xiàn)方式出現(xiàn)呢?我們可用看到在第一種實(shí)現(xiàn)方式中無(wú)法向單例模式的構(gòu)造方法傳遞參數(shù),而使用滯后初始化的方式,我們可用在調(diào)用getInstance()方法的時(shí)候向方法中傳遞參數(shù)。
凡事有好處必然有壞處,滯后初始化的一個(gè)弊病就是在多線程或分布式的環(huán)境下有可能出現(xiàn)混亂:
“有時(shí)在某些情況下,使用Singleton并不能達(dá)到Singleton的目的,如有多個(gè)Singleton對(duì)象同時(shí)被不同的類裝入器裝載;在EJB這樣的分布式系統(tǒng)中使用也要注意這種情況,因?yàn)镋JB是跨服務(wù)器,跨JVM的。” --摘自www.jdon.com-《GoF 23種設(shè)計(jì)模式解析》
“在多線程環(huán)境下,我們無(wú)法保證一個(gè)方法能夠持續(xù)運(yùn)行到結(jié)束,其他線程的方法才開(kāi)始運(yùn)行。因而可能存在這樣一種情形:兩個(gè)線程幾乎同時(shí)嘗試初始化單例類。假設(shè)第一個(gè)方法發(fā)現(xiàn)單例為空,而第二個(gè)方法在此刻開(kāi)始運(yùn)行,它也會(huì)發(fā)現(xiàn)該單例為空。接下來(lái),這兩個(gè)方法都將對(duì)該單例進(jìn)行初始化。” --摘自《Java設(shè)計(jì)模式》
二、單例模式在多線程下的安全實(shí)現(xiàn)
《Java并發(fā)編程》一書(shū)建議使用屬于當(dāng)前類的鎖進(jìn)行同步,代碼如下:
public class Singleton {
private static Singleton instance = null;
// 注意這里的static非常重要,如果為對(duì)象變量則因?yàn)榇嬖诙喾菘截惗鸩坏较拗频淖饔?/font>
private static Object classLock = Singleton.class;
public static Singleton getInstance() {
//這個(gè)方法比上面有所改進(jìn),不用每次都進(jìn)行生成對(duì)象,只是第一次
//使用時(shí)生成實(shí)例!
synchronized(classLock){
if (instance==null)
instance=new Singleton();
return instance;
}
}
}
}
在第一個(gè)線程開(kāi)始滯后初始化的時(shí)候,如果有另一線程也準(zhǔn)備開(kāi)始初始化,這時(shí)候,第二個(gè)線程將停止執(zhí)行,等待獲取對(duì)象classLock的鎖。當(dāng)?shù)诙€(gè)線程獲取這個(gè)鎖并開(kāi)始執(zhí)行初始化的時(shí)候,它會(huì)發(fā)現(xiàn)該單例已不再為空(因?yàn)橹淮嬖谠擃惖奈ㄓ袑?shí)例,我們可以使用單個(gè)靜態(tài)鎖)
三、使用單例模式的另外一些注意點(diǎn)
·單例模式類不能實(shí)現(xiàn)Clonable接口,以防被克隆而產(chǎn)生多個(gè)實(shí)例
·單例模式類不能實(shí)現(xiàn)Serializable接口,以防被序列化而產(chǎn)生多個(gè)實(shí)例
-------------------------------------------------------------
生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
posted on 2008-01-03 22:31
Paul Lin 閱讀(1231)
評(píng)論(0) 編輯 收藏 所屬分類:
模式與重構(gòu)