Java設(shè)計模式—單例模式

概念

java中單例模式是一種常見的設(shè)計模式,單例模式的寫法有好幾種级乍,比較常見的有:懶漢式單例、餓漢式單例帚湘。
單例模式有以下特點(diǎn):
  1玫荣、單例類只能有一個實例。
  2大诸、單例類必須自己創(chuàng)建自己的唯一實例崇决。
  3、單例類必須給所有其他對象提供這一實例底挫。
單例模式可以確保某個類只有一個實例恒傻,而且自行實例化并向整個系統(tǒng)提供這個實例。

想必有很多人和我一樣建邓,最早接觸到的設(shè)計模式就是單例模式盈厘。和其他設(shè)計模式相比較,單例模式確實顯得簡單一些官边,而且它也是Android開發(fā)當(dāng)中最常用的設(shè)計模式之一沸手,從許多開源框架源碼中都能看見單例的應(yīng)用外遇。廢話不多說,直接上代碼契吉。

一跳仿、首先看看單例模式在幾個著名框架內(nèi)的使用情況:

1、EventBus

/** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

2捐晶、Android-Universal-Image-Loader

/** Returns singleton class instance */
    public static ImageLoader getInstance() {
        if (instance == null) {
            synchronized (ImageLoader.class) {
                if (instance == null) {
                    instance = new ImageLoader();
                }
            }
        }
        return instance;
    }

從上面的代碼中可以看到菲语,大神們造輪子比較青睞的寫法還是雙重校驗鎖。盡管受到大神們青睞惑灵,但這并不意味著這種寫法是完全沒有問題的山上。

二、接下來我們分析一下單例模式的各種寫法:

懶漢式

//懶漢式單例類.在第一次調(diào)用的時候?qū)嵗约?  
public class Singleton {  
    private Singleton() {}  
    private static Singleton single=null;  
    //靜態(tài)工廠方法   
    public static Singleton getInstance() {  
         if (single == null) {    
             single = new Singleton();  
         }    
        return single;  
    }  
}  

但是以上懶漢式單例的實現(xiàn)沒有考慮線程安全問題英支,它是線程不安全的佩憾,并發(fā)環(huán)境下很可能出現(xiàn)多個Singleton實例,要實現(xiàn)線程安全干花,有以下兩種方式妄帘,都是對getInstance這個方法改造,保證了懶漢式單例的線程安全池凄。

1寄摆、給方法添加同步synchronized

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

同步方法不可避免地會對性能造成一定的影響,因此使用時要慎重考慮修赞。

2婶恼、雙重校驗鎖

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

盡管雙重檢查鎖定背后的理論是完美的。不幸地是柏副,現(xiàn)實完全不同勾邦。雙重檢查鎖定的問題是:并不能保證它會在單處理器或多處理器計算機(jī)上順利運(yùn)行。因此我們通過給singleton加上volatile 關(guān)鍵字來應(yīng)對性能敏感的場合割择。
檢查鎖定失敗的問題并不歸咎于 JVM 中的實現(xiàn) bug眷篇,而是歸咎于 Java 平臺內(nèi)存模型。內(nèi)存模型允許所謂的“無序?qū)懭搿崩笥荆@也是這些習(xí)語失敗的一個主要原因蕉饼。
想了解具體原因請參考 Java單例模式中雙重檢查鎖的問題

3、靜態(tài)內(nèi)部類(Holder模式)
我們既希望利用餓漢模式中靜態(tài)變量的方便和線程安全玛歌;又希望通過懶加載規(guī)避資源浪費(fèi)昧港。Holder模式滿足了這兩點(diǎn)要求:核心仍然是靜態(tài)變量,足夠方便和線程安全支子;通過靜態(tài)的Holder類持有真正實例创肥,間接實現(xiàn)了懶加載。

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

相對于餓漢模式,Holder模式僅增加了一個靜態(tài)內(nèi)部類的成本叹侄,與飽漢的變種3效果相當(dāng)(略優(yōu)),都是比較受歡迎的實現(xiàn)方式。同樣建議考慮禽捆。

餓漢式

//餓漢式單例類.在類初始化時得湘,已經(jīng)自行實例化   
public class Singleton1 {  
    private Singleton1() {}  
    private static final Singleton1 single = new Singleton1();  
    //靜態(tài)工廠方法   
    public static Singleton1 getInstance() {  
        return single;  
    }  
}  

餓漢式在類創(chuàng)建的同時就已經(jīng)創(chuàng)建好一個靜態(tài)的對象供系統(tǒng)使用(static關(guān)鍵字的功勞)淘正,并且巧妙地利用final關(guān)鍵字保證以后不再改變囤采,所以天生是線程安全的蕉毯。

Android Studio可以直接新建單例模式類代虾,使用的就是餓漢式寫法棉磨,我們可以看看。

新建單例模式類.png

代碼如下:

/**
 * Created by QianWei on 2017/11/23.
 */
public class Singleton1 {
    private static Singleton1 ourInstance = new Singleton1();

    public static Singleton1 getInstance() {
        return ourInstance;
    }

    private Singleton1() {
    }
}

餓漢式和懶漢式區(qū)別

從名字上來說,餓漢和懶漢,餓漢就是類一旦加載蓄喇,就把單例初始化完成妆偏,保證getInstance的時候叔锐,單例是已經(jīng)存在的了,而懶漢比較懶解取,只有當(dāng)調(diào)用getInstance的時候蔓肯,才回去初始化這個單例蔗包。

另外從以下兩點(diǎn)再區(qū)分以下這兩種方式:

1、線程安全:
餓漢式天生就是線程安全的误澳,可以直接用于多線程而不會出現(xiàn)問題脓匿,懶漢式本身是非線程安全的米母,為了實現(xiàn)線程安全有幾種寫法铁瞒,分別是上面的1慧耍、2芍碧、3泌豆,這三種實現(xiàn)在資源加載和性能方面有些區(qū)別蔬浙。

2畴博、資源加載和性能:
餓漢式在類創(chuàng)建的同時就實例化一個靜態(tài)對象出來,不管之后會不會使用這個單例庶艾,都會占據(jù)一定的內(nèi)存颖榜,但是相應(yīng)的且蓬,在第一次調(diào)用時速度也會更快,因為其資源已經(jīng)初始化完成恶阴,
而懶漢式顧名思義诈胜,會延遲加載,在第一次使用該單例的時候才會實例化對象出來冯事,第一次調(diào)用時要做初始化焦匈,如果要做的工作比較多,性能上會有些延遲昵仅,之后就和餓漢式一樣了缓熟。
至于1、2、3這三種實現(xiàn)又有些區(qū)別够滑,
第1種垦写,在方法調(diào)用上加了同步,雖然線程安全了彰触,但是每次都要同步梯澜,會影響性能,畢竟99%的情況下是不需要同步的渴析,
第2種晚伙,在getInstance中做了兩次null檢查,確保了只有第一次調(diào)用單例的時候才會做同步俭茧,這樣也是線程安全的咆疗,同時避免了每次都同步的性能損耗
第3種,利用了classloader的機(jī)制來保證初始化instance時只有一個線程母债,所以也是線程安全的午磁,同時沒有性能損耗,所以一般我傾向于使用這一種毡们。

三迅皇、總結(jié)

上面的分析都忽略了反射和序列化的問題。通過反射或序列化衙熔,我們?nèi)匀荒軌蛟L問到私有構(gòu)造器登颓,創(chuàng)建新的實例破壞單例模式。此時红氯,只有枚舉模式能天然防范這一問題框咙。


參考鏈接
JAVA設(shè)計模式之單例模式
程序猿應(yīng)該記住的幾條基本規(guī)則
面試中單例模式有幾種寫法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市痢甘,隨后出現(xiàn)的幾起案子喇嘱,更是在濱河造成了極大的恐慌,老刑警劉巖塞栅,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件者铜,死亡現(xiàn)場離奇詭異,居然都是意外死亡放椰,警方通過查閱死者的電腦和手機(jī)作烟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來庄敛,“玉大人俗壹,你說我怎么就攤上這事≡蹇荆” “怎么了绷雏?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵头滔,是天一觀的道長。 經(jīng)常有香客問我涎显,道長坤检,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任期吓,我火速辦了婚禮早歇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘讨勤。我一直安慰自己箭跳,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布潭千。 她就那樣靜靜地躺著谱姓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪刨晴。 梳的紋絲不亂的頭發(fā)上屉来,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天,我揣著相機(jī)與錄音狈癞,去河邊找鬼茄靠。 笑死,一個胖子當(dāng)著我的面吹牛蝶桶,可吹牛的內(nèi)容都是我干的慨绳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼莫瞬,長吁一口氣:“原來是場噩夢啊……” “哼儡蔓!你這毒婦竟也來了郭蕉?” 一聲冷哼從身側(cè)響起疼邀,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎召锈,沒想到半個月后旁振,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涨岁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年拐袜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片梢薪。...
    茶點(diǎn)故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡蹬铺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秉撇,到底是詐尸還是另有隱情甜攀,我是刑警寧澤秋泄,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站规阀,受9級特大地震影響恒序,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜谁撼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一歧胁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧厉碟,春花似錦喊巍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至袄秩,卻和暖如春阵翎,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背之剧。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工郭卫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人背稼。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓贰军,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蟹肘。 傳聞我的和親對象是個殘疾皇子词疼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評論 2 348

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