不使用synchronized和lock,如何實現一個線程安全的單例?

以前,在個人微信公衆號(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;  
    }  
}  
複製代碼

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

餓漢

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;  
    }  
}  
複製代碼

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

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

答:以上幾種實現方式,都是藉助了ClassLoader的線程安全機制。cdn

先解釋清楚爲何說都是藉助了ClassLoaderget

從後往前說,先說兩個餓漢,其實都是經過定義靜態的成員變量,以保證instance能夠在類初始化的時候被實例化。那爲啥讓instance在類初始化的時候被實例化就能保證線程安全了呢?由於類的初始化是由ClassLoader完成的,這其實就是利用了ClassLoader的線程安全機制啊。同步

再說靜態內部類,這種方式和兩種餓漢方式只有細微差異,只是作法上稍微優雅一點。這種方式是Singleton類被裝載了,instance不必定被初始化。由於SingletonHolder類沒有被主動使用,只有顯示經過調用getInstance方法時,纔會顯示裝載SingletonHolder類,從而實例化instance。。。可是,原理和餓漢同樣。源碼

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

至此,咱們說清楚了,各位看官的回答都是利用了ClassLoader的線程安全機制。至於爲何ClassLoader加載類是線程安全的,這裏能夠先直接回答:ClassLoaderloadClass方法在加載類的時候使用了synchronized關鍵字。也正是由於這樣, 除非被重寫,這個方法默認在整個裝載過程當中都是同步的(線程安全的)。(詳情見:深度分析Java的ClassLoader機制(源碼級別)


哈哈哈哈!!!~因此呢,這裏能夠說,你們的回答都只答對了一半。雖然沒有顯示使用synchronizedlock,可是仍是間接的用到了!!!!

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

相關文章
相關標籤/搜索