雙重檢驗鎖實現方式安全
public class Singleton { //定義一個私有的空構造方法,防止直接用new實例化 private Singleton() {} private static volatile Singleton singleton = null; public static Singleton getInstance() { if(singleton == null) { synchronized(Singleton.class) { if(singleton == null) { singleton = new Singleton(); } } } return singleton; } }
雙重校驗鎖,從代碼的中能夠看出,在同步代碼塊外多了一層instance爲空的判斷,因爲單例對象只須要建立一次,若是後面再次調用getInstance()只須要直接返回單例對象便可,所以,在大部分狀況下,調用getInstance()都不會執行到同步代碼塊,從而提升的程序性能。可是還須要考慮一種狀況,假如兩個線程A、B,線程A執行了if(instance == null)語句,它會任務單例對象沒有建立,此時線程切到B也執行了一樣的語句,B也認爲單例對象沒有建立,而後兩個線程一次執行同步代碼塊,並分別建立了一個單例對象,因此爲了解決這個問題,還須要在同步代碼塊中增長if(instance == null)的語句判斷,避免重複建立的問題。多線程
能夠發現instance變量使用了volatile修飾,這樣作的目的在於禁止指令重排序優化,所謂指令重排序優化是指在不改變原語義的前提下,經過調整指令的執行順序讓程序運行的更快,JVM中並無規定編譯器優化的相關內容,也就是說JVM能夠自由的鏡像指令重排序優化,這樣會致使Singleton和將對象賦值給instance字段的順序是不肯定的。在摸個線程建立單例對象時,在構造方法被調用以前,就爲該對象分配了內存空間將對象的字段設置爲默認值。此時就能夠將分配的內存地址賦值給instance字段了,然而該對象可能尚未初始化,若緊接着另外一個線程來調用getInsatance,取到的就是狀態不正確的對象,程序就會出錯。volatile的一個語義是禁止指令重排序優化,也就是保證了instance遍歷被賦值的時候對象已是初始化過的,從而避免上面說的問題。併發
靜態內部類實現方式性能
public class Singleton{ private static class SingletonHolder{ public static Singleton instance = new Singleton(); } private Singleton(){} public static Singleton newInstance(){ return SingletonHolder.instance; }
}
這種方式一樣利用了類加載機制來保證只建立一個instance實例。它與餓漢模式同樣,也是利用了類加載機制,所以不存在多線程併發的問題。不同的是,它是在內部類裏面去建立對象實例。這樣的話,只要應用中不使用內部類,JVM就不會去加載這個單例類,也就不會建立單例對象,從而實現懶漢式的延遲加載。也就是說這種方式能夠同時保證延遲加載和線程安全。優化