不使用synchronized和lock,如何實(shí)現(xiàn)一個線程安全的單例?
不使用synchronized和lock,如何實(shí)現(xiàn)一個線程安全的單例?
回答最多的是靜態(tài)內(nèi)部類和枚舉。很好,這兩種確實(shí)可以實(shí)現(xiàn)。
枚舉
- public enum Singleton {
- INSTANCE;
- public void whateverMethod() {
- }
- }
靜態(tài)內(nèi)部類
- 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;
- }
- }
(更多單例實(shí)現(xiàn)方式見:單例模式的七種寫法)
問:這幾種實(shí)現(xiàn)單例的方式的真正的原理是什么呢?
答:以上幾種實(shí)現(xiàn)方式,都是借助了ClassLoader的線程安全機(jī)制。
先解釋清楚為什么說都是借助了ClassLoader。
從后往前說,先說兩個餓漢,其實(shí)都是通過定義靜態(tài)的成員變量,以保證instance可以在類初始化的時候被實(shí)例化。那為啥讓instance在類初始化的時候被實(shí)例化就能保證線程安全了呢?因?yàn)轭惖某跏蓟怯蒀lassLoader完成的,這其實(shí)就是利用了ClassLoader的線程安全機(jī)制啊。
再說靜態(tài)內(nèi)部類,這種方式和兩種餓漢方式只有細(xì)微差別,只是做法上稍微優(yōu)雅一點(diǎn)。這種方式是Singleton類被裝載了,instance不一定被初始化。因?yàn)镾ingletonHolder類沒有被主動使用,只有顯示通過調(diào)用getInstance方法時,才會顯示裝載SingletonHolder類,從而實(shí)例化instance。。。但是,原理和餓漢一樣。
***說枚舉,其實(shí),如果把枚舉類進(jìn)行反序列化,你會發(fā)現(xiàn)他也是使用了static final來修飾每一個枚舉項(xiàng)。(詳情見:深度分析Java的枚舉類型—-枚舉的線程安全性及序列化問題)
至此,我們說清楚了,各位看官的回答都是利用了ClassLoader的線程安全機(jī)制。至于為什么ClassLoader加載類是線程安全的,這里可以先直接回答:ClassLoader的loadClass方法在加載類的時候使用了synchronized關(guān)鍵字。也正是因?yàn)檫@樣, 除非被重寫,這個方法默認(rèn)在整個裝載過程中都是同步的(線程安全的)。(詳情見:深度分析Java的ClassLoader機(jī)制(源碼級別))
哈哈哈哈!!!~所以呢,這里可以說,大家的回答都只答對了一半。雖然沒有顯示使用synchronized和lock,但是還是間接的用到了!!!!
那么,這里再問一句:不使用synchronized和lock,如何實(shí)現(xiàn)一個線程安全的單例?
【本文是51CTO專欄作者Hollis的原創(chuàng)文章,作者微信公眾號Hollis(ID:hollischuang)】