Java中的四種單例模式
單例模式是最容易理解的設(shè)計(jì)模式之一,介紹Java中單例模式的四種寫法。
1.基本單例模式
public class Singleton{
private static Singleton instance=new Singleton();
private Singleton(){}
pulic static Singleton getInstance(){
return instance;
}
}
特點(diǎn):餓漢式會(huì)提前進(jìn)行實(shí)例化,沒(méi)有延遲加載,不管是否使用都會(huì)有一個(gè)已經(jīng)初始化的實(shí)例在內(nèi)存中,但不會(huì)出現(xiàn)懶漢式中的多線程問(wèn)題。
2.延遲加載單例模式
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null)
return instance=new Singleton();
else
return instance;
}
}
特點(diǎn):實(shí)現(xiàn)了延遲加載,但在多線程情況下可能會(huì)出現(xiàn)問(wèn)題,不能保證線程安全。
3.線程安全的延遲加載單例模式
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static sychronized Singleton getInstance(){
if(instance==null)
return instance=new Singleton();
else
return instance;
}
}
特點(diǎn):實(shí)現(xiàn)了線程安全,但是由于synchronized限制了整個(gè)getInstance方法,而我們只是希望在new Singleton()時(shí)進(jìn)行加鎖,因此這種寫法會(huì)導(dǎo)致效率不高。于是有人提出以下寫法:
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static sychronized Singleton getInstance(){
if(instance==null)
sychronized(Singleton.class){
if(instance==null)
instance=new Singleton();
}
return instance;
}
}
思路是只需同步初始化那部分代碼,這就是所謂的雙檢鎖機(jī)制,很可惜這種寫法在很多平臺(tái)和優(yōu)化編譯器中無(wú)法編譯通過(guò),原因在于,instance=new Singleton()這行代碼在不同的編譯器中的行為是無(wú)法預(yù)知的,很有可能出現(xiàn)以下初始化情況:
變量初始化通過(guò)兩個(gè)步驟實(shí)現(xiàn):1.給變量分配內(nèi)存空間;2.調(diào)用構(gòu)造函數(shù)來(lái)初始化成員變量。
假設(shè)線程A和B都在調(diào)用getInstance,線程A先進(jìn)入,在執(zhí)行完1步驟后被踢出了CPU,然后線程B進(jìn)入,B看到的instance已經(jīng)不是null了(內(nèi)存已經(jīng)分配),于是它開(kāi)始放心大膽的使用instance,但這個(gè)是錯(cuò)誤的,因?yàn)閕nstance的成員變量還都是缺省值,A還沒(méi)來(lái)得及調(diào)用構(gòu)造方法來(lái)完成instance的初始化。
4.靜態(tài)內(nèi)部類的單例模式
public class Singleton{
private Singleton(){}
private static class Inner{
private static Singleton instanceHolder=new Singleton();
}
public static Singleton getInstacen(){
return Inner.instanceHolder;
}
}
由于內(nèi)部類在編譯完成后也是一個(gè)單獨(dú)的class文件,因此在不使用的情況下Inner類是不會(huì)被加載的。同時(shí),JVM保證在類加載的過(guò)程中static代碼塊在多線程或者單線程下都正確執(zhí)行,且僅執(zhí)行一次。解決了延遲加載以及線程安全的問(wèn)題。