不使用synchronized和lock,如何實現一個執行緒安全的單例?

HollisChuang發表於2018-08-22

之前,在我的微信公眾號(hollishcuang)上發了一條問題:不使用synchronizedlock,如何實現一個執行緒安全的單例?

瞬間收到了數百條回覆。回答最多的是靜態內部類和列舉。很好,這兩種確實可以實現。

列舉

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}  
複製程式碼

靜態內部類

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}  
複製程式碼

還有人回答的很簡單:餓漢。很好,這個也是對的。

餓漢

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}  
複製程式碼

餓漢變種

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}  
複製程式碼

(更多單例實現方式見:單例模式的七種寫法

問:這幾種實現單例的方式的真正的原理是什麼呢?

答:以上幾種實現方式,都是藉助了ClassLoader的執行緒安全機制。

先解釋清楚為什麼說都是藉助了ClassLoader

從後往前說,先說兩個餓漢,其實都是通過定義靜態的成員變數,以保證instance可以在類初始化的時候被例項化。那為啥讓instance在類初始化的時候被例項化就能保證執行緒安全了呢?因為類的初始化是由ClassLoader完成的,這其實就是利用了ClassLoader的執行緒安全機制啊。

再說靜態內部類,這種方式和兩種餓漢方式只有細微差別,只是做法上稍微優雅一點。這種方式是Singleton類被裝載了,instance不一定被初始化。因為SingletonHolder類沒有被主動使用,只有顯示通過呼叫getInstance方法時,才會顯示裝載SingletonHolder類,從而例項化instance。。。但是,原理和餓漢一樣。

最後說列舉,其實,如果把列舉類進行反序列化,你會發現他也是使用了static final來修飾每一個列舉項。(詳情見:深度分析Java的列舉型別—-列舉的執行緒安全性及序列化問題

至此,我們說清楚了,各位看官的回答都是利用了ClassLoader的執行緒安全機制。至於為什麼ClassLoader載入類是執行緒安全的,這裡可以先直接回答:ClassLoaderloadClass方法在載入類的時候使用了synchronized關鍵字。也正是因為這樣, 除非被重寫,這個方法預設在整個裝載過程中都是同步的(執行緒安全的)。(詳情見:深度分析Java的ClassLoader機制(原始碼級別)


哈哈哈哈!!!~所以呢,這裡可以說,大家的回答都只答對了一半。雖然沒有顯示使用synchronizedlock,但是還是間接的用到了!!!!

那麼,這裡再問一句:不使用synchronized和lock,如何實現一個執行緒安全的單例?答案見:不使用synchronized和lock,如何實現一個執行緒安全的單例?(二)

不使用synchronized和lock,如何實現一個執行緒安全的單例?

相關文章