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

單例模式

單例模式有以下特點(diǎn):

  • 單例類(lèi)只能有一個(gè)實(shí)例
  • 單例類(lèi)必須自己創(chuàng)建自己的唯一實(shí)例
  • 單例類(lèi)必須給所有其他對(duì)象提供這一實(shí)例

優(yōu)點(diǎn):

  • 只有一個(gè)實(shí)例,可以減少內(nèi)存開(kāi)銷(xiāo)
  • 避免對(duì)資源的多重占用
  • 設(shè)置全局資源訪問(wèn)

單例的幾種寫(xiě)法:

/************手寫(xiě)一定要自己動(dòng)手手寫(xiě) 面試你會(huì)發(fā)現(xiàn)全是手寫(xiě)一個(gè)單例模式開(kāi)始***********/

  • 懶漢式

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

/**
 * <pre>
 *     author : lzy
 *     e-mail : zanyang.lin@newbeeair.com
 *     time   : 2017/07/11
 *     desc   : 單例模式的幾種寫(xiě)法
 * </pre>
 */

public class Singleton {
    private static Singleton single=null;
    private  Singleton (){}
    public static Singleton getIntstance(){
        if (single==null){
            single=new Singleton();
        }
        return single;
    }

}

這種寫(xiě)法沒(méi)有考慮線程安全問(wèn)題宦芦,在并發(fā)環(huán)境下可能出現(xiàn)多個(gè)singleton實(shí)例

保證線程安全修改如下

1拍柒、解決了線程同步 不過(guò)效率較低

public class Singleton {
    private static Singleton single=null;
    private  Singleton (){}
    
    //添加同步鎖
    public static synchronized Singleton getIntstance(){
        if (single==null){
            single=new Singleton();
        }
        return single;
    }

}

2喇嘱、在并發(fā)量不高 安全性不高情況下可以很好運(yùn)行 同時(shí)避免了每次都同步的性能損耗

package com.example.singleton;

/**
 * <pre>
 *     author : lzy
 *     e-mail : zanyang.lin@newbeeair.com
 *     time   : 2017/07/11
 *     desc   : 單例模式的幾種寫(xiě)法
 * </pre>
 */

public class Singleton {
    private volatile static Singleton single = null;

    private Singleton() {
    }

    //添加同步鎖
    public static  Singleton getIntstance() {
        if (single == null) {

            //雙重檢查鎖
            synchronized (Singleton.class) {
                if (single == null) {
                    single = new Singleton();
                }
            }

        }
        return single;
    }



}



volatile

  • 原子操作
    簡(jiǎn)單來(lái)說(shuō)魏铅,原子操作(atomic)就是不可分割的操作,在計(jì)算機(jī)中盏档,就是指不會(huì)因?yàn)榫€程調(diào)度被打斷的操作澈吨。比如把敢,簡(jiǎn)單的賦值是一個(gè)原子操作:m = 6; // 這是個(gè)原子操作
      假如m原先的值為0,那么對(duì)于這個(gè)操作谅辣,要么執(zhí)行成功m變成了6修赞,要么是沒(méi)執(zhí)行m還是0,而不會(huì)出現(xiàn)諸如m=3這種中間態(tài)——即使是在并發(fā)的線程中桑阶。而柏副,聲明并賦值就不是一個(gè)原子操作:int n = 6; // 這不是一個(gè)原子操作對(duì)于這個(gè)語(yǔ)句,至少有兩個(gè)操作:①聲明一個(gè)變量n②給n賦值為6——這樣就會(huì)有一個(gè)中間狀態(tài):變量n已經(jīng)被聲明了但是還沒(méi)有被賦值的狀態(tài)联逻〈瓿叮——這樣检痰,在多線程中包归,由于線程執(zhí)行順序的不確定性,如果兩個(gè)線程都使用m铅歼,就可能會(huì)導(dǎo)致不穩(wěn)定的結(jié)果出現(xiàn)公壤。
  • 指令重排
    簡(jiǎn)單來(lái)說(shuō),就是計(jì)算機(jī)為了提高執(zhí)行效率椎椰,會(huì)做的一些優(yōu)化厦幅,在不影響最終結(jié)果的情況下,可能會(huì)對(duì)一些語(yǔ)句的執(zhí)行順序進(jìn)行調(diào)整慨飘。比如确憨,這一段代碼:
int a ;   // 語(yǔ)句1 
a = 8 ;   // 語(yǔ)句2
int b = 9 ;     // 語(yǔ)句3
int c = a + b ; // 語(yǔ)句4

正常來(lái)說(shuō),對(duì)于順序結(jié)構(gòu)瓤的,執(zhí)行的順序是自上到下休弃,也即1234。但是圈膏,由于指令重排
的原因塔猾,因?yàn)椴挥绊懽罱K的結(jié)果,所以稽坤,實(shí)際執(zhí)行的順序可能會(huì)變成3124或者1324丈甸。由于語(yǔ)句3和4沒(méi)有原子性的問(wèn)題糯俗,語(yǔ)句3和語(yǔ)句4也可能會(huì)拆分成原子操作,再重排睦擂〉孟妫——也就是說(shuō),對(duì)于非原子性的操作祈匙,在不影響最終結(jié)果的情況下忽刽,其拆分成的原子操作可能會(huì)被重新排列執(zhí)行順序。
  OK夺欲,了解了原子操作和指令重排的概念之后跪帝,我們?cè)倮^續(xù)看雙重校驗(yàn)鎖方式單例模式代碼的問(wèn)題。下面這段話直接從陳皓的文章(深入淺出單實(shí)例SINGLETON設(shè)計(jì)模式)中復(fù)制而來(lái):主要在于singleton = new Singleton()這句些阅,這并非是一個(gè)原子操作伞剑,事實(shí)上在 JVM 中這句話大概做了下面 3 件事情:
1). 給 singleton 分配內(nèi)存
2). 調(diào)用 Singleton 的構(gòu)造函數(shù)來(lái)初始化成員變量,形成實(shí)例
3). 將singleton對(duì)象指向分配的內(nèi)存空間(執(zhí)行完這步 singleton才是非 null 了)但是在 JVM 的即時(shí)編譯器中存在指令重排序的優(yōu)化市埋。
  也就是說(shuō)上面的第二步和第三步的順序是不能保證的黎泣,最終的執(zhí)行順序可能是 1-2-3 也可能是 1-3-2。如果是后者缤谎,則在 3 執(zhí)行完畢抒倚、2 未執(zhí)行之前,被線程二搶占了坷澡,這時(shí) instance 已經(jīng)是非 null 了(但卻沒(méi)有初始化)托呕,所以線程二會(huì)直接返回 instance,然后使用频敛,然后順理成章地報(bào)錯(cuò)项郊。
  再稍微解釋一下,就是說(shuō)斟赚,由于有一個(gè)『instance已經(jīng)不為null但是仍沒(méi)有完成初始化』的中間狀態(tài)着降,而這個(gè)時(shí)候,如果有其他線程剛好運(yùn)行到第一層if (instance == null)這里拗军,這里讀取到的instance已經(jīng)不為null了任洞,所以就直接把這個(gè)中間狀態(tài)的instance拿去用了,就會(huì)產(chǎn)生問(wèn)題发侵。這里的關(guān)鍵在于——線程T1對(duì)instance的寫(xiě)操作沒(méi)有完成交掏,線程T2就執(zhí)行了讀操作。

volatilel作用 禁止指令重排

volatile關(guān)鍵字的一個(gè)作用是禁止指令重排器紧,把instance聲明為volatile之后耀销,對(duì)它的寫(xiě)操作就會(huì)有一個(gè)內(nèi)存屏障(什么是內(nèi)存屏障?),這樣熊尉,在它的賦值完成之前罐柳,就不用會(huì)調(diào)用讀操作。
  注意:volatile阻止的不singleton = new Singleton()這句話內(nèi)部[1-2-3]的指令重排狰住,而是保證了在一個(gè)寫(xiě)操作([1-2-3])完成之前张吉,不會(huì)調(diào)用讀操作(if (instance == null))。

3催植、靜態(tài)內(nèi)部類(lèi) 延遲加載肮蛹,線程安全 減少內(nèi)存消耗

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

}
  • 餓漢式

public class Singleton{
private static final Singleton single=new Singleton();
private Singleton(){}
public static Singleton getIntstance(){
return single;

}

}

兩者區(qū)別:

  • 餓漢式是類(lèi)一旦加載就把單例初始化完成 保證getinstance的時(shí)候 是已經(jīng)存在的
  • 懶漢只有當(dāng)調(diào)用getInstance的時(shí)候才去初始化這個(gè)單例
  • 餓漢天生是線程安全的
  • 懶漢本身非線程安全 為了實(shí)現(xiàn)線程安全有幾種寫(xiě)法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市创南,隨后出現(xiàn)的幾起案子伦忠,更是在濱河造成了極大的恐慌,老刑警劉巖稿辙,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件昆码,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡邻储,警方通過(guò)查閱死者的電腦和手機(jī)赋咽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)吨娜,“玉大人脓匿,你說(shuō)我怎么就攤上這事』略” “怎么了陪毡?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袱瓮。 經(jīng)常有香客問(wèn)我缤骨,道長(zhǎng)爱咬,這世上最難降的妖魔是什么尺借? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮精拟,結(jié)果婚禮上燎斩,老公的妹妹穿的比我還像新娘。我一直安慰自己蜂绎,他們只是感情好栅表,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著师枣,像睡著了一般怪瓶。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上践美,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天洗贰,我揣著相機(jī)與錄音找岖,去河邊找鬼。 笑死敛滋,一個(gè)胖子當(dāng)著我的面吹牛许布,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播绎晃,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蜜唾,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了庶艾?” 一聲冷哼從身側(cè)響起袁余,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咱揍,沒(méi)想到半個(gè)月后泌霍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡述召,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年朱转,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片积暖。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藤为,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出夺刑,到底是詐尸還是另有隱情缅疟,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布遍愿,位于F島的核電站存淫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏沼填。R本人自食惡果不足惜桅咆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坞笙。 院中可真熱鬧岩饼,春花似錦、人聲如沸薛夜。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)梯澜。三九已至寞冯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吮龄。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工檬某, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人螟蝙。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓恢恼,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親胰默。 傳聞我的和親對(duì)象是個(gè)殘疾皇子场斑,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

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

  • 單例模式(Singleton Pattern)是眾多設(shè)計(jì)模式中較為簡(jiǎn)單的一個(gè),同時(shí)它也是面試時(shí)經(jīng)常被提及的問(wèn)題牵署,如...
    廖少少閱讀 564評(píng)論 0 1
  • 前言 本文主要參考 那些年漏隐,我們一起寫(xiě)過(guò)的“單例模式”。 何為單例模式奴迅? 顧名思義青责,單例模式就是保證一個(gè)類(lèi)僅有一個(gè)...
    tandeneck閱讀 2,517評(píng)論 1 8
  • 單例模式(Singleton) 單例對(duì)象(Singleton)是一種常用的設(shè)計(jì)模式。在Java應(yīng)用中取具,單例對(duì)象能保...
    曉瘋閱讀 500評(píng)論 0 0
  • 第十章 由于白天的失誤脖隶,晚上回到酒店余景灝飯也沒(méi)有顧得上吃飯就抱著劇本看了起來(lái)。既然他天生有缺陷暇检,就只能付出比別人...
    星如雨雨雨閱讀 269評(píng)論 0 0
  • 小魚(yú)喜歡上了另一個(gè)部門(mén)的男同事产阱。 那個(gè)男孩英挺帥氣,工作表現(xiàn)突出块仆,見(jiàn)到他的第一眼构蹬,小魚(yú)就被牢牢吸引。 小魚(yú)一向行事...
    如果是什么果閱讀 575評(píng)論 4 6