Java 單例模式

? ? 說(shuō)到單例模式大家應(yīng)該都不陌生,就是程序在運(yùn)行過(guò)程中岛心,一個(gè)類只允許有一個(gè)實(shí)例存在于內(nèi)存當(dāng)中。例如線程池管理類碾盟、緩存管理類妆丘、某個(gè)模塊內(nèi)存管理類等锄俄。這些類之所以只允許一個(gè)實(shí)例,除了內(nèi)存消耗方面的考慮外勺拣,還有程序正確性的考量奶赠。
? ? 例如,假設(shè)圖片內(nèi)存管理類有兩個(gè)實(shí)例A和B药有,那在實(shí)例A和B上分別有兩個(gè)內(nèi)存緩存毅戈,假設(shè)把一張圖片存放在A,去B拿的時(shí)候就拿到空了愤惰,顯然不合理苇经。
? ? 單例模式要求一個(gè)類永遠(yuǎn)只能返回同一個(gè)實(shí)例,實(shí)現(xiàn)的步驟有兩個(gè):
? ? 1.將類的構(gòu)造方法的修飾符改為private宦言,這樣外部將無(wú)法實(shí)例化這個(gè)類扇单;
? ? 2.該類對(duì)外提供一個(gè)獲取實(shí)例的方法,內(nèi)部有一個(gè)指向本類的對(duì)象引用奠旺,當(dāng)引用為空時(shí)蜘澜,實(shí)例化這個(gè)類并返回,否則直接返回引用凉倚。
下面給出一些單例模式的實(shí)現(xiàn):

1.餓漢模式:在類加載的時(shí)候直接實(shí)例化【可使用】
public class Singleton {

    private static volatile Singleton sInstance = new Singleton();

    private Singleton() {
    }

    public static Singleton instance() {
        return sInstance;
    }
}

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單兼都,在類被加載時(shí)就實(shí)例化,避免了線程同步問(wèn)題稽寒;
缺點(diǎn):類加載時(shí)就實(shí)例化,沒(méi)有懶加載趟章,后面可能用不到這個(gè)實(shí)例杏糙,造成了內(nèi)存資源的浪費(fèi)。

2.懶漢模式(同步方法)【不推薦】
public class Singleton {

    private static Singleton sInstance;

    private Singleton() {}

    public static synchronized Singleton instance() {
        if (sInstance == null) {
            sInstance = new Singleton();
        }
        return sInstance;
    }
}

優(yōu)點(diǎn):懶加載蚓土,線程同步宏侍;
缺點(diǎn):盡管JDK7對(duì)synchronized做了優(yōu)化(偏向鎖、輕量級(jí)鎖蜀漆、自旋鎖谅河、鎖去除),但是即使對(duì)象已經(jīng)實(shí)例化了,每次也都要進(jìn)行同步操作绷耍,易造成堵塞吐限,效率低。

3.懶漢模式(雙重檢查)【不推薦】
public class Singleton {

    private static volatile Singleton sInstance;
    private static Object mObject = new Object();

    private Singleton() {
    }

    public static Singleton instance() {
        if (sInstance == null) {
            synchronized (mObject) {
                if (sInstance == null) {
                    sInstance = new Singleton();
                }
            }
        }
        return sInstance;
    }
}

優(yōu)點(diǎn):兩次check null褂始,中間加必要的同步诸典,實(shí)現(xiàn)了線程安全,創(chuàng)建了對(duì)象崎苗。接下來(lái)當(dāng)?shù)谝徊脚袛嗖粸榭盏臅r(shí)候狐粱,就直接返回了,效率也比較高胆数。
缺點(diǎn):注意到sInstance前面使用volatile關(guān)鍵字修飾了嗎肌蜻?
這里說(shuō)下題外話,JVM在new一個(gè)對(duì)象的時(shí)候必尼,有如下3個(gè)步驟:

  • a. 給Singleton的實(shí)例分配內(nèi)存
  • b. 調(diào)用構(gòu)造函數(shù)宋欺,初始化成員屬性字段
  • c. 將sInstance指向這塊內(nèi)存
    首先這個(gè)過(guò)程不是原子操作,其次JVM允許指令重排胰伍,當(dāng)執(zhí)行的順序是a - b - c的時(shí)候是沒(méi)問(wèn)題齿诞,但是當(dāng)執(zhí)行的順序是a - c - b時(shí),線程A執(zhí)行到c骂租,讓出了CPU執(zhí)行時(shí)間片祷杈,此時(shí)線程B判斷sInstance是不為空,直接返回引用渗饮,但是此時(shí)對(duì)象還沒(méi)創(chuàng)建但汞,用的時(shí)候豈不是很尷尬?NPE很有可能即將隨之而來(lái)互站。這就是DCL失效問(wèn)題私蕾,這種問(wèn)題難以跟蹤、復(fù)現(xiàn)胡桃,出現(xiàn)的概率小踩叭。
    volatile能在sInstance插入內(nèi)存屏障,防止指令重排翠胰,使每次讀取都得去主內(nèi)存讀取容贝,因此能防止這樣的問(wèn)題。
    但指令重排什么的是JVM為程序運(yùn)行做的優(yōu)化之一之景,這樣子做不太好斤富,并且每次都得去主內(nèi)存讀取,或多或少也影響到性能锻狗。這種優(yōu)化在《Java 高并發(fā)程序設(shè)計(jì)》等書籍上被稱為“丑陋的優(yōu)化”满力,因此不推薦使用這種焕参。
4. 靜態(tài)內(nèi)部類【推薦】
public class Singleton {

    private Singleton() {
    }

    public static Singleton instance() {
        return SHolder.sInstance;
    }

    private static class SHolder {
        private static final Singleton sInstance = new Singleton();
    }
}

這個(gè)看著跟餓漢模式有幾分相似?是懶漢加載嗎油额?
類的靜態(tài)屬性只會(huì)在第一次加載類的時(shí)候初始化叠纷,因此sInstance只會(huì)在第一次調(diào)用SHolder.sInstance,即外部調(diào)用instance()時(shí)悔耘,才會(huì)初始化讲岁。
優(yōu)點(diǎn):巧妙利用類的初始化時(shí)機(jī),避免了線程同步問(wèn)題(類在初始化時(shí)衬以,其它線程無(wú)法進(jìn)入)缓艳,同時(shí)實(shí)現(xiàn)了懶加載;

5. 枚舉【推薦】
public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}

借助JDK1.5中添加的枚舉來(lái)實(shí)現(xiàn)單例模式看峻。不僅能避免多線程同步問(wèn)題阶淘,而且還能防止反序列化重新創(chuàng)建新的對(duì)象』ゼ耍可能是因?yàn)槊杜e在JDK1.5中才添加溪窒,所以在實(shí)際項(xiàng)目開發(fā)中,很少見人這么寫過(guò)冯勉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澈蚌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子灼狰,更是在濱河造成了極大的恐慌宛瞄,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件交胚,死亡現(xiàn)場(chǎng)離奇詭異份汗,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蝴簇,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門杯活,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人熬词,你說(shuō)我怎么就攤上這事旁钧。” “怎么了荡澎?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵均践,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我摩幔,道長(zhǎng),這世上最難降的妖魔是什么鞭铆? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任或衡,我火速辦了婚禮焦影,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘封断。我一直安慰自己斯辰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布坡疼。 她就那樣靜靜地躺著彬呻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪柄瑰。 梳的紋絲不亂的頭發(fā)上闸氮,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音教沾,去河邊找鬼蒲跨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛授翻,可吹牛的內(nèi)容都是我干的或悲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼堪唐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼巡语!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起淮菠,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤男公,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后兜材,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體理澎,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年曙寡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糠爬。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡举庶,死狀恐怖执隧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情户侥,我是刑警寧澤镀琉,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站蕊唐,受9級(jí)特大地震影響屋摔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜替梨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一钓试、第九天 我趴在偏房一處隱蔽的房頂上張望装黑。 院中可真熱鬧,春花似錦弓熏、人聲如沸恋谭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)疚颊。三九已至,卻和暖如春信认,著一層夾襖步出監(jiān)牢的瞬間材义,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工狮杨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留母截,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓橄教,卻偏偏與公主長(zhǎng)得像清寇,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子护蝶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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