劍指offer面試題2:實(shí)現(xiàn)Singleton模式

實(shí)現(xiàn)單例模式應(yīng)該是面試中比較常見的問題诫硕,下面簡單講講幾種單例模式的實(shí)現(xiàn)方式你踩。

餓漢式

public class Singleton{
    public static Singleton instance = new Singleton();
    
    private Singleton();
    
    public static Singleton getInstance(){
        return instance;
    }
}

優(yōu)點(diǎn):避免線程同步產(chǎn)生的問題,沒有加鎖折柠,效率更高宾娜。
缺點(diǎn):在類加載的時(shí)候就初始化對象,浪費(fèi)內(nèi)存扇售。

懶漢式

V1.0

public class Singleton{
    public static Singleton instance = null;
    
    private Singleton();
    
    public static Singleton getInstance(){
        if(instance == null){
            intance = new Singleton();
        }
        return instance;
    }
}

優(yōu)點(diǎn):需要時(shí)才創(chuàng)建對象前塔,不會(huì)存在內(nèi)存浪費(fèi)。
缺點(diǎn):多線程環(huán)境下不能正常工作承冰,容易創(chuàng)建出多個(gè)對象华弓。

V2.0

public class Singleton{
    public static Singleton instance = null;
    
    private Singleton();
    
    public static Singleton getInstance(){
        synchronized(this.class){
            if(instance == null){
                intance = new Singleton();
            }
        }
        return instance;
    }
}

優(yōu)點(diǎn):在方法加上synchronized關(guān)鍵字,保證了線程相對安全困乒,不會(huì)創(chuàng)建出多個(gè)對象寂屏。
缺點(diǎn):synchronized會(huì)導(dǎo)致效率低下。

V3.0 雙重校驗(yàn)鎖

public class Singleton{
    private static Singleton instance = null;
    
    private Singleton();

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(this.class){
                if(instance == null){
                    singleton = new Singleton();
                }
            }
        }
        return instance;
    }
}

優(yōu)點(diǎn):既保證線程相對安全娜搂,又不會(huì)導(dǎo)致效率低下的問題迁霎。
缺點(diǎn)
singleton = new Singleton()這句,這并非是一個(gè)原子操作百宇,事實(shí)上在 JVM 中這句話大概做了下面 3 件事情欧引。

  1. 給 singleton 分配內(nèi)存
  2. 調(diào)用 Singleton 的構(gòu)造函數(shù)來初始化成員變量,形成實(shí)例
  3. 將singleton對象指向分配的內(nèi)存空間(執(zhí)行完這步 singleton才是非 null 了)
    但是在 JVM 的即時(shí)編譯器中存在指令重排序的優(yōu)化恳谎。也就是說上面的第二步和第三步的順序是不能保證的,最終的執(zhí)行順序可能是 1-2-3也可能是 1-3-2。如果是后者因痛,則在 3 執(zhí)行完畢婚苹、2 未執(zhí)行之前,被線程二搶占了鸵膏,這時(shí) instance 已經(jīng)是非 null 了(但卻沒有初始化)膊升,所以線程二會(huì)直接返回 instance,然后使用谭企,然后順理成章地報(bào)錯(cuò)廓译。

對此,我們只需要把singleton聲明成 volatile 就可以了债查。

V4.0

public class Singleton{
    private voletile static Singleton instance = null;
    
    private Singleton();

    public static Singleton getInstance(){
        if(instance == null){
            synchronized(this.class){
                if(instance == null){
                    singleton = new Singleton();
                }
            }
        }
        return instance;
    }
}

volatile有兩個(gè)作用:

  1. 這個(gè)變量不會(huì)在多線程中存在多個(gè)副本非区,直接從內(nèi)存中讀取
  2. 防止指令重排序。也就是說盹廷,在 volatile 變量的賦值操作后面會(huì)有一個(gè)內(nèi)存屏障(生成的匯編代碼上)征绸,讀操作不會(huì)被重排序到內(nèi)存屏障之前。

靜態(tài)內(nèi)部類

public class Singleton{
    private Singleton();
    
    private static class SingletonHandler{
        private static final Singleton INSTANCE = new Singleton();
    }

    public static getInstance(){
        return SingletonHandler.INSTANCE;    
    }
}

上面這種方式俄占,仍然使用JVM本身機(jī)制保證了線程安全問題管怠;由于 SingletonHolder 是私有的,除了 getInstance() 之外沒有辦法訪問它缸榄。因此它只有在getInstance()被調(diào)用時(shí)才會(huì)真正創(chuàng)建渤弛;同時(shí)讀取實(shí)例的時(shí)候不會(huì)進(jìn)行同步,沒有性能缺陷甚带;也不依賴 JDK 版本她肯。

枚舉

public enum SingletonV6{
    INSTANCE;
}

默認(rèn)枚舉實(shí)例的創(chuàng)建是線程安全的,所以不需要擔(dān)心線程安全的問題欲低,而且因?yàn)镴VM會(huì)阻止反射獲取枚舉類的私有構(gòu)造方法辕宏,無法通過反射的方式構(gòu)建對象。但是在枚舉中的其他任何方法的線程安全由程序員自己負(fù)責(zé)砾莱。

總結(jié)

“陳式第一定理” : “無論你的代碼寫得有多好瑞筐,其只能在特定的范圍內(nèi)工作,超出這個(gè)范圍就要出Bug了”

如果我們的這個(gè)Singleton類是一個(gè)關(guān)于我們程序配置信息的類腊瑟。我們需要它有序列化的功能聚假,那么,當(dāng)反序列化的時(shí)候闰非,我們將無法控制別人不多次反序列化膘格。

不過,我們可以利用一下Serializable接口的readResolve()方法财松,比如

class SingletonV7 implements Serializable {
    // ......
    // ......
    protected Object readResolve() {
        return getInstance();
    }

    public static Object getInstance(){
        return instance;
    }
}

參考:

深入淺出單實(shí)例SINGLETON設(shè)計(jì)模式

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘪贱,一起剝皮案震驚了整個(gè)濱河市纱控,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌菜秦,老刑警劉巖甜害,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異球昨,居然都是意外死亡尔店,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門主慰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嚣州,“玉大人,你說我怎么就攤上這事共螺「秒龋” “怎么了?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵璃谨,是天一觀的道長沙庐。 經(jīng)常有香客問我,道長佳吞,這世上最難降的妖魔是什么拱雏? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮底扳,結(jié)果婚禮上铸抑,老公的妹妹穿的比我還像新娘。我一直安慰自己衷模,他們只是感情好鹊汛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阱冶,像睡著了一般刁憋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上木蹬,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天至耻,我揣著相機(jī)與錄音,去河邊找鬼镊叁。 笑死尘颓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的晦譬。 我是一名探鬼主播疤苹,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼敛腌!你這毒婦竟也來了卧土?” 一聲冷哼從身側(cè)響起惫皱,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎夸溶,沒想到半個(gè)月后逸吵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡缝裁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了足绅。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捷绑。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氢妈,靈堂內(nèi)的尸體忽然破棺而出粹污,到底是詐尸還是另有隱情,我是刑警寧澤首量,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布壮吩,位于F島的核電站,受9級特大地震影響加缘,放射性物質(zhì)發(fā)生泄漏鸭叙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一拣宏、第九天 我趴在偏房一處隱蔽的房頂上張望沈贝。 院中可真熱鬧,春花似錦勋乾、人聲如沸宋下。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽学歧。三九已至,卻和暖如春各吨,著一層夾襖步出監(jiān)牢的瞬間枝笨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工绅你, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伺帘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓忌锯,卻偏偏與公主長得像伪嫁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子偶垮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容