不使用synchronized和lock,如何實現一個線程安全的單例?
不使用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,如何實現一個線程安全的單例?
【本文是51CTO專欄作者Hollis的原創文章,作者微信公眾號Hollis(ID:hollischuang)】