java 單例設(shè)計(jì)模式

單例是應(yīng)用開發(fā)中一種設(shè)計(jì)模式只磷,主要應(yīng)用場(chǎng)景為:當(dāng)且僅當(dāng)系統(tǒng)中只能保留一個(gè)對(duì)象時(shí)使用抱完。本文提出4中可以在生產(chǎn)環(huán)境中使用的單例設(shè)計(jì)模式狗热。推薦使用enum的方式卖宠。

應(yīng)用場(chǎng)景

例如一下應(yīng)用場(chǎng)景[1]
1滤灯、 Windows的Task Manager(任務(wù)管理器)就是很典型的單例模式(這個(gè)很熟悉吧)坪稽,想想看,是不是呢鳞骤,你能打開兩個(gè)windows task manager嗎窒百?

2、網(wǎng)站的瀏覽人數(shù)統(tǒng)計(jì)豫尽,一般也是采用單例模式實(shí)現(xiàn)篙梢,否則難以同步。

3美旧、應(yīng)用程序的日志應(yīng)用渤滞,一般都何用單例模式實(shí)現(xiàn),這一般是由于共享的日志文件一直處于打開狀態(tài)榴嗅,因?yàn)橹荒苡幸粋€(gè)實(shí)例去操作妄呕,否則內(nèi)容不好追加。

//todo
在joshua block 的《effective java second edition》 一書中給出了三種單例設(shè)計(jì)模式

1嗽测、采用靜態(tài)變量:

public class TaskManager {
        public static final TaskManager INSTANCE = new TaskManager ();
        private TaskManager (){}
        //...
}

這種寫法使用了私有的構(gòu)造方法绪励。來保證只能有一個(gè)實(shí)例,但是這種方法也有例外情況,因?yàn)檫胫啵憧梢酝ㄟ^反射來調(diào)用私有構(gòu)造方法疏魏。這個(gè)時(shí)候你可以拋出異常。以下代碼僅作為參考厅贪。

public class TaskManager {
    public static final TaskManager INSTANCE = new TaskManager();

    private TaskManager() {
        if (INSTANCE != null) {
            try {
                throw new Exception("An object already exists");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //...
}

2蠢护、采用靜態(tài)方法

public class TaskManager {
    private static final TaskManager INSTANCE = new TaskManager();

    private TaskManager() {}

    public static TaskManager getINSTANCE() {
        return INSTANCE;
    }

    //...
}

3、采用enum的方式

這種模式是目前最佳的养涮,因?yàn)椋?br> 1葵硕、JVM會(huì)保證enum不能被反射并且構(gòu)造器方法只執(zhí)行一次眉抬。
2、此方法無償提供了序列化機(jī)制懈凹,絕對(duì)防止反序列化時(shí)多次實(shí)例化蜀变。
3、運(yùn)行時(shí)(compile-time )創(chuàng)建對(duì)象(懶加載) // todo 關(guān)于cmpile-time和run-time有時(shí)間我單獨(dú)寫一篇文章介评。

enum是jdk5的特性库北,現(xiàn)在(2017)web應(yīng)用普遍在jdk6、7们陆、8寒瓦,所以可以放心使用。

目前最佳的方式是使用接口的方式(解耦):

interface Resource {
    Object doSomething();
}

public enum SomeThing implements Resource {
    INSTANCE {
        @Override
        public Object doSomething() {
            return "I am a Singleton nstance";
        }
    };
}

class Demo {
    public static void main(String[] args) {
        System.out.println(SomeThing.INSTANCE.doSomething());
    }
}

或者不使用接口的形式

public enum SomeThing {
    INSTANCE;

    public void doSomething() {
        System.out.println("INSTANCE = " + INSTANCE);
    }

}

class Demo {
    public static void main(String[] args) {
        SomeThing.INSTANCE.doSomething();
    }
}

也有人用其他的方式坪仇,我對(duì)這種方法持強(qiáng)烈反對(duì),具體可以參考文獻(xiàn)4杂腰,以下代碼僅做參考

class Resource {
}

public enum SomeThing {
    INSTANCE;
    private Resource instance;

    SomeThing() {
        instance = new Resource();
    }

    public Resource getInstance() {
        return instance;
    }
}

class Demo {
    public static void main(String[] args) {
        System.out.println(SomeThing.INSTANCE.getInstance());
    }
}

在其他文章中有提到“懶漢”、“惡漢”的名詞椅文,其實(shí)懶漢主要就是"懶"加載[注:指在使用時(shí)裝載喂很,不使用時(shí)不進(jìn)行裝載]

有人提出這種懶漢設(shè)計(jì)

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

很顯然這種設(shè)計(jì)線程不安全皆刺,一般不會(huì)使用。
有人又提出了懶漢改進(jìn)的方法羡蛾,使其線程安全。

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

這種寫法能夠在多線程中很好的工作林说,而且看起來它也具備很好的lazy loading煎殷,但是,遺憾的是,因?yàn)槭侵亓考?jí)鎖腿箩,效率很低。

于是有人提出了雙重校驗(yàn)鎖機(jī)制,這個(gè)用的也比較多珠移。

下面代碼就是用double checked locking 方法實(shí)現(xiàn)的單例,這里的getInstance()方法要檢查兩次钧惧,確保是否實(shí)例INSTANCE是否為null或者已經(jīng)實(shí)例化了暇韧,這也是為什么叫double checked locking 模式。

/**
* Singleton pattern example with Double checked Locking
*/
public class DoubleCheckedLockingSingleton{
     private volatile DoubleCheckedLockingSingleton INSTANCE;
 
     private DoubleCheckedLockingSingleton(){}
 
     public DoubleCheckedLockingSingleton getInstance(){
         if(INSTANCE == null){
            synchronized(DoubleCheckedLockingSingleton.class){
                //double checking Singleton instance
                if(INSTANCE == null){
                    INSTANCE = new DoubleCheckedLockingSingleton();
                }
            }
         }
         return INSTANCE;
     }
}

參考文獻(xiàn):
[1] Jason Cai, 設(shè)計(jì)模式之——單例模式(Singleton)的常見應(yīng)用場(chǎng)景
[2] cantellow, 單例模式的七種寫法
[3] Javarevisited, 單例模式中為什么用枚舉更好
[4] natsumi, Java枚舉實(shí)現(xiàn)單例模式
[5] zejian_浓瞪,深入理解Java枚舉類型(enum)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末懈玻,一起剝皮案震驚了整個(gè)濱河市乾颁,隨后出現(xiàn)的幾起案子艺栈,更是在濱河造成了極大的恐慌,老刑警劉巖湿右,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罚勾,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡尖殃,警方通過查閱死者的電腦和手機(jī)丈莺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門分衫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人蚪战,你說我怎么就攤上這事铐懊。” “怎么了科乎?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)捏萍。 經(jīng)常有香客問我,道長(zhǎng)令杈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任逗噩,我火速辦了婚禮跌榔,結(jié)果婚禮上异雁,老公的妹妹穿的比我還像新娘僧须。我一直安慰自己纲刀,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布示绊。 她就那樣靜靜地躺著,像睡著了一般耻台。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上盆耽,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音摄杂,去河邊找鬼。 笑死析恢,一個(gè)胖子當(dāng)著我的面吹牛墨坚,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播映挂,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼柑船,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼帽撑!你這毒婦竟也來了鞍时?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤逆巍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后锐极,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡味咳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年檬嘀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鸳兽。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出全陨,到底是詐尸還是另有隱情,我是刑警寧澤辱姨,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布雨涛,位于F島的核電站枢舶,受9級(jí)特大地震影響替久,放射性物質(zhì)發(fā)生泄漏凉泄。R本人自食惡果不足惜蚯根,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望颅拦。 院中可真熱鬧,春花似錦距帅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽广匙。三九已至允趟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間潮剪,已是汗流浹背分唾。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弧蝇,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓沙峻,卻偏偏與公主長(zhǎng)得像两芳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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

  • 1.應(yīng)用場(chǎng)景: 當(dāng)需要保證類在內(nèi)存中的對(duì)象唯一性佑笋,可以使用單例模式,不想創(chuàng)建多個(gè)實(shí)例浪費(fèi)資源,或者避免多個(gè)實(shí)例由于...
    發(fā)光的魚閱讀 265評(píng)論 0 0
  • 心得體會(huì) 今天主要寫了關(guān)于撲克的demo,這對(duì)我來說很復(fù)雜愁茁,總而言之鹅很,就是今天上了那么久的課嘶居,課后我回想起來促煮,在我...
    寧曉鴦閱讀 841評(píng)論 0 1
  • 為了能讓接下來的幾篇設(shè)計(jì)模式串起來菠齿,這篇就先寫單例設(shè)計(jì)模式。初學(xué)java基礎(chǔ)課程的時(shí)候泞当,無論是在課上還是自學(xué)看視頻...
    ironman327閱讀 337評(píng)論 0 0
  • 文|民國(guó)十年 說起水鬼民珍,不得不說游泳盗飒。 游泳嚷量,一件看似在生活里相當(dāng)常見的事情逆趣。不管是大人還是小孩,好像都能插上幾嘴...
    門后有人閱讀 890評(píng)論 4 10
  • 逗霸君閱讀 273評(píng)論 3 10