以前,在個人微信公衆號(hollishcuang)上發了一條問題:不使用synchronized
和lock
,如何實現一個線程安全的單例?安全
瞬間收到了數百條回覆。回答最多的是靜態內部類和枚舉。很好,這兩種確實能夠實現。微信
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
先解釋清楚爲何說都是藉助了ClassLoader
。get
從後往前說,先說兩個餓漢,其實都是經過定義靜態的成員變量,以保證instance
能夠在類初始化的時候被實例化。那爲啥讓instance
在類初始化的時候被實例化就能保證線程安全了呢?由於類的初始化是由ClassLoader
完成的,這其實就是利用了ClassLoader
的線程安全機制啊。同步
再說靜態內部類,這種方式和兩種餓漢方式只有細微差異,只是作法上稍微優雅一點。這種方式是Singleton
類被裝載了,instance
不必定被初始化。由於SingletonHolder
類沒有被主動使用,只有顯示經過調用getInstance
方法時,纔會顯示裝載SingletonHolder
類,從而實例化instance
。。。可是,原理和餓漢同樣。源碼
最後說枚舉,其實,若是把枚舉類進行反序列化,你會發現他也是使用了static
final
來修飾每個枚舉項。(詳情見:深度分析Java的枚舉類型—-枚舉的線程安全性及序列化問題)it
至此,咱們說清楚了,各位看官的回答都是利用了ClassLoader
的線程安全機制。至於爲何ClassLoader
加載類是線程安全的,這裏能夠先直接回答:ClassLoader
的loadClass
方法在加載類的時候使用了synchronized
關鍵字。也正是由於這樣, 除非被重寫,這個方法默認在整個裝載過程當中都是同步的(線程安全的)。(詳情見:深度分析Java的ClassLoader機制(源碼級別))
哈哈哈哈!!!~因此呢,這裏能夠說,你們的回答都只答對了一半。雖然沒有顯示使用synchronized
和lock
,可是仍是間接的用到了!!!!
那麼,這裏再問一句:不使用synchronized和lock,如何實現一個線程安全的單例?答案見:不使用synchronized和lock,如何實現一個線程安全的單例?(二)