之前,在我的微信公眾號(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;
}
}
複製程式碼
還有人回答的很簡單:餓漢。很好,這個也是對的。
餓漢
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
載入類是執行緒安全的,這裡可以先直接回答:ClassLoader
的loadClass
方法在載入類的時候使用了synchronized
關鍵字。也正是因為這樣, 除非被重寫,這個方法預設在整個裝載過程中都是同步的(執行緒安全的)。(詳情見:深度分析Java的ClassLoader機制(原始碼級別))
哈哈哈哈!!!~所以呢,這裡可以說,大家的回答都只答對了一半。雖然沒有顯示使用synchronized
和lock
,但是還是間接的用到了!!!!
那麼,這裡再問一句:不使用synchronized和lock,如何實現一個執行緒安全的單例?答案見:不使用synchronized和lock,如何實現一個執行緒安全的單例?(二)