Java單例實現及分析

雙重檢驗鎖實現方式安全

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就不會去加載這個單例類,也就不會建立單例對象,從而實現懶漢式的延遲加載。也就是說這種方式能夠同時保證延遲加載和線程安全。優化

相關文章
相關標籤/搜索