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