第一種(懶漢达吞,線程不安全):
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種寫(xiě)法lazy loading很明顯培他,但是致命的是在多線程不能正常工作。
第二種(懶漢草添,線程安全):
public class Singleton {
private static Singleton instance;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
這種寫(xiě)法能夠在多線程中很好的工作驶兜,而且看起來(lái)它也具備很好的lazy loading,但是远寸,遺憾的是抄淑,效率很低,99%情況下不需要同步驰后。
第三種(餓漢):
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
這種方式基于classloder機(jī)制避免了多線程的同步問(wèn)題肆资,不過(guò),instance在類裝載時(shí)就實(shí)例化灶芝,雖然導(dǎo)致類裝載的原因有很多種郑原,在單例模式中大多數(shù)都是調(diào)用getInstance方法, 但是也不能確定有其他的方式(或者其他的靜態(tài)方法)導(dǎo)致類裝載夜涕,這時(shí)候初始化instance顯然沒(méi)有達(dá)到lazy loading的效果犯犁。
第四種(餓漢,變種):
public class Singleton {
private Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton (){}
public static Singleton getInstance() {
return this.instance;
}
}
表面上看起來(lái)差別挺大女器,其實(shí)更第三種方式差不多酸役,都是在類初始化即實(shí)例化instance。
第五種(靜態(tài)內(nèi)部類):
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
這種方式同樣利用了classloder的機(jī)制來(lái)保證初始化instance時(shí)只有一個(gè)線程驾胆,它跟第三種和第四種方式不同的是(很細(xì)微的差別):第三種和第四種方式是只要Singleton類被裝載了涣澡,那么instance就會(huì)被實(shí)例化(沒(méi)有達(dá)到lazy loading效果),而這種方式是Singleton類被裝載了丧诺,instance不一定被初始化入桂。因?yàn)镾ingletonHolder類沒(méi)有被主動(dòng)使用,只有顯示通過(guò)調(diào)用getInstance方法時(shí)驳阎,才會(huì)顯示裝載SingletonHolder類抗愁,從而實(shí)例化instance惕艳。想象一下,如果實(shí)例化instance很消耗資源驹愚,我想讓他延遲加載远搪,另外一方面,我不希望在Singleton類加載時(shí)就實(shí)例化逢捺,因?yàn)槲也荒艽_保Singleton類還可能在其他的地方被主動(dòng)使用從而被加載谁鳍,那么這個(gè)時(shí)候?qū)嵗痠nstance顯然是不合適的。這個(gè)時(shí)候劫瞳,這種方式相比第三和第四種方式就顯得很合理倘潜。
第六種(枚舉):
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
這種方式是Effective Java作者Josh Bloch 提倡的方式,它不僅能避免多線程同步問(wèn)題志于,而且還能防止反序列化重新創(chuàng)建新的對(duì)象涮因,可謂是很堅(jiān)強(qiáng)的壁壘啊,不過(guò)伺绽,個(gè)人認(rèn)為由于1.5中才加入enum特性养泡,用這種方式寫(xiě)不免讓人感覺(jué)生疏,在實(shí)際工作中奈应,我也很少看見(jiàn)有人這么寫(xiě)過(guò)澜掩。
第七種(雙重校驗(yàn)鎖):
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
這個(gè)是第二種方式的升級(jí)版,俗稱雙重檢查鎖定杖挣,詳細(xì)介紹請(qǐng)查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html
在JDK1.5之后肩榕,雙重檢查鎖定才能夠正常達(dá)到單例效果。
總結(jié)
有兩個(gè)問(wèn)題需要注意:
1.如果單例由不同的類裝載器裝入惩妇,那便有可能存在多個(gè)單例類的實(shí)例株汉。假定不是遠(yuǎn)端存取,例如一些servlet容器對(duì)每個(gè)servlet使用完全不同的類裝載器歌殃,這樣的話如果有兩個(gè)servlet訪問(wèn)一個(gè)單例類乔妈,它們就都會(huì)有各自的實(shí)例。
2.如果Singleton實(shí)現(xiàn)了java.io.Serializable接口挺份,那么這個(gè)類的實(shí)例就可能被序列化和復(fù)原褒翰。不管怎樣,如果你序列化一個(gè)單例類的對(duì)象匀泊,接下來(lái)復(fù)原多個(gè)那個(gè)對(duì)象,那你就會(huì)有多個(gè)單例類的實(shí)例朵你。
對(duì)第一個(gè)問(wèn)題修復(fù)的辦法是:
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
對(duì)第二個(gè)問(wèn)題修復(fù)的辦法是:
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() {
}
private Object readResolve() {
return INSTANCE;
}
}
對(duì)我來(lái)說(shuō)各聘,我比較喜歡第三種和第五種方式,簡(jiǎn)單易懂抡医,而且在JVM層實(shí)現(xiàn)了線程安全(如果不是多個(gè)類加載器環(huán)境)躲因,一般的情況下早敬,我會(huì)使用第三種方式,只有在要明確實(shí)現(xiàn)lazy loading效果時(shí)才會(huì)使用第五種方式大脉,另外搞监,如果涉及到反序列化創(chuàng)建對(duì)象時(shí)我會(huì)試著使用枚舉的方式來(lái)實(shí)現(xiàn)單例,不過(guò)镰矿,我一直會(huì)保證我的程序是線程安全的琐驴,而且我永遠(yuǎn)不會(huì)使用第一種和第二種方式,如果有其他特殊的需求秤标,我可能會(huì)使用第七種方式绝淡,畢竟,JDK1.5已經(jīng)沒(méi)有雙重檢查鎖定的問(wèn)題了苍姜。
不過(guò)一般來(lái)說(shuō)牢酵,第一種不算單例,第四種和第三種就是一種衙猪,如果算的話馍乙,第五種也可以分開(kāi)寫(xiě)了。所以說(shuō)垫释,一般單例都是五種寫(xiě)法潘拨。懶漢,惡漢饶号,雙重校驗(yàn)鎖铁追,枚舉和靜態(tài)內(nèi)部類。
轉(zhuǎn)載請(qǐng)注明出處:http://cantellow.iteye.com/blog/838473