Java單例模式


定義

一個(gè)類只有一個(gè)實(shí)例池凄,自行實(shí)例化并提供給整個(gè)系統(tǒng)枣察。

基本思路

將該類構(gòu)造函數(shù)私有化魄眉,并通過(guò)靜態(tài)方法獲取一個(gè)唯一實(shí)例砰盐,獲取過(guò)程保證線程安全。

懶漢式線程不安全寫法

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getInstance() {

if (instance == null) { //分水嶺

instance = new Singleton();

}

return instance;

}

}

假設(shè)有A坑律、B線程同時(shí)調(diào)用getInstance方法岩梳,在分水嶺同時(shí)執(zhí)行完判斷后,由于CPU時(shí)間片切換晃择,假設(shè)A線程得到執(zhí)行權(quán)冀值,繼續(xù)向下執(zhí)行,實(shí)例化了對(duì)象宫屠,此時(shí)instance不為空,而B線程又做了實(shí)例化工作,就會(huì)導(dǎo)致實(shí)例不唯一乌逐。

懶漢式線程安全


public class Singleton {

private static Singleton instance;

private Singleton (){}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

在getInstance()方法前加上synchronized浙踢,使整個(gè)方法同步洛波,就可以保證線程安全,避免多實(shí)例的問(wèn)題缚窿,但是這樣并不高效倦零,因?yàn)樵诘谝淮握{(diào)用過(guò)getInstance()方法后扫茅,instance不為空育瓜,之后再調(diào)用該方法都要走同步躏仇,這樣會(huì)消耗不必要的資源。

Double Check Lock(雙重檢驗(yàn)鎖)

public class Singleton {

private static Singleton instance;

private Singleton (){}

public static Singleton getSingleton() {

if (instance == null) {? ? ? ? ? ? ? ? ? ? ? ? //Single Checked

synchronized (Singleton.class) {

if (instance == null) {? ? ? ? ? ? ? ? //Double Checked

instance = new Singleton();

}

}

}

return instance ;

}

}

這里為啥要做兩次檢驗(yàn)?zāi)鼗燮穑课覀兿葋?lái)看下册倒,如果去掉第一層檢驗(yàn)會(huì)怎樣


public static Singleton getSingleton() {

synchronized (Singleton.class) {

if (instance == null) {

instance = new Singleton();

}

return instance ;

}

當(dāng)有兩個(gè)線程同時(shí)調(diào)用getSingleton()方法時(shí),由于同步機(jī)制的存在灿意,假設(shè)此時(shí)A線程得到CPU的執(zhí)行權(quán)崇呵,走到同步塊中執(zhí)行域慷,執(zhí)行了instance = new Singleton()后汗销,退出同步語(yǔ)句塊弛针,此時(shí)instance已經(jīng)不為null了削茁,這時(shí)候B線程獲得CPU執(zhí)行權(quán)也進(jìn)入同步塊茧跋,就會(huì)被instance == null的判斷擋在外面了瘾杭。

所以就算不要第一層檢驗(yàn)也是可以實(shí)現(xiàn)多線程安全的單例捍岳,那為毛還要寫锣夹?原來(lái)這里涉及到性能問(wèn)題,上面懶漢式線程安全的寫法也是安全的变勇,但是每次調(diào)用都要走同步會(huì)影響性能搀绣,這里也一樣链患。我們希望這里的new Singleton()只執(zhí)行一次,如果少了第一層的判斷瓶您,每次有線程進(jìn)入 getInstance()時(shí),均會(huì)執(zhí)行鎖定操作來(lái)實(shí)現(xiàn)線程同步呀袱,這是非常耗費(fèi)性能的贸毕,而多了第一層檢驗(yàn)夜赵,只有第一次instance == null會(huì)執(zhí)行鎖定操作明棍,之后的調(diào)用直接return instance就行寇僧。

再看如果少了第二層檢驗(yàn)摊腋,其實(shí)這就跟第一種懶漢式線程不安全寫法一樣沸版,在多線程并發(fā)的情況下不能達(dá)到保持單例的效果。

那么雙重檢驗(yàn)機(jī)制是否就能完美解決問(wèn)題推穷?其實(shí)不然,其原因在于instance = new Singleton();這句并非是個(gè)原子操作蟹腾,當(dāng)JVM運(yùn)行到這一句是,分別作了如下操作

1.在堆中為 instance 分配內(nèi)存

2.調(diào)用 Singleton的構(gòu)造函數(shù)初始化成員變量

3.將instance對(duì)象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)

如果上面的操作按1-2-3順序執(zhí)行,那就沒(méi)啥問(wèn)題炉爆,但是由于JVM的即時(shí)編譯器中存在指令重排序的優(yōu)化,上面的操作有可能是1-3-2。這會(huì)出現(xiàn)啥情況郁稍?


public static Singleton getSingleton() {

if (instance == null) {? ? ? ? //p2

synchronized (Singleton.class) {

if (instance == null) {

instance = new Singleton(); //p1

}

}

}

return instance ;

}

假設(shè)線程A此時(shí)走到p1處赦政,JVM按照1-3-2的順序?qū)嵗痠nstance,才執(zhí)行完1-3,來(lái)不及執(zhí)行2耀怜,線程B獲得了CPU執(zhí)行權(quán)也調(diào)用了getInstance()方法恢着,并走到p2處,此時(shí)instance不為null,線程B就屁顛屁顛拿著得到的實(shí)例回去干活了财破,但是里頭的成員變量還沒(méi)初始化掰派,這就尷尬了,報(bào)錯(cuò)也就理所當(dāng)然了左痢。

要解決這問(wèn)題只要把instance設(shè)置成volatile靡羡,禁止重排序優(yōu)化,以上面的例子抖锥,不管是1-2-3還是1-3-2亿眠,讀取必須在操作完全執(zhí)行完后才能進(jìn)行。


public class Singleton {

private volatile static Singleton instance;

private Singleton (){}

public static Singleton getSingleton() {

if (instance == null) {? ? ? ? ? ? ? ? ? ? ? ? //Single Checked

synchronized (Singleton.class) {

if (instance == null) {? ? ? ? ? ? ? ? //Double Checked

instance = new Singleton();

}

}

}

return instance ;

}

}

然而在Java5以前的版本使用volatile還是有問(wèn)題磅废,也不能完全避免重排序纳像。

惡漢式


public class Singleton{

private static final Singleton instance = new Singleton();

private Singleton(){}

public static Singleton getInstance(){

return instance;

}

}

由于instance被static和final修飾,在該類第一次被加載到內(nèi)存中時(shí)就會(huì)對(duì)instance實(shí)例化拯勉,這樣保證了單例竟趾。缺點(diǎn)是就算其他地方?jīng)]有調(diào)用getInstance()方法憔购,也會(huì)創(chuàng)建實(shí)例。

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


public class Singleton {

private static class SingletonHolder {

private static final Singleton INSTANCE = new Singleton();

}

private Singleton (){}

public static final Singleton getInstance() {

return SingletonHolder.INSTANCE;

}

}

靜態(tài)內(nèi)部類就比較好的彌補(bǔ)了惡漢式單例不足岔帽,也是比較推崇的寫法玫鸟。

枚舉


public enum EnumSingleton{

INSTANCE;

}

創(chuàng)建枚舉默認(rèn)就是線程安全的,所以不需要擔(dān)心線程同步犀勒,而且還能防止反序列化導(dǎo)致重新創(chuàng)建新的對(duì)象屎飘。通過(guò)EnumSingleton.INSTANCE來(lái)訪問(wèn)實(shí)例,只是平時(shí)很少看到代碼中有人這么寫贾费。

本文作者:HuYounger

本文標(biāo)題:Java單例模式

本文鏈接:http://rkhcy.github.io/2017/03/29/Java單例模式/

發(fā)布時(shí)間:2017年3月29日 - 00時(shí)03分

版權(quán)聲明:本文由 HuYounger 原創(chuàng)钦购,采用保留署名-非商業(yè)性使用-禁止演繹 4.0-國(guó)際許可協(xié)議

轉(zhuǎn)載請(qǐng)保留以上聲明信息!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末褂萧,一起剝皮案震驚了整個(gè)濱河市押桃,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌导犹,老刑警劉巖唱凯,帶你破解...
    沈念sama閱讀 211,948評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異谎痢,居然都是意外死亡磕昼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門舶得,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)掰烟,“玉大人,你說(shuō)我怎么就攤上這事沐批∪移铮” “怎么了?”我有些...
    開封第一講書人閱讀 157,490評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵九孩,是天一觀的道長(zhǎng)先馆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)躺彬,這世上最難降的妖魔是什么煤墙? 我笑而不...
    開封第一講書人閱讀 56,521評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮宪拥,結(jié)果婚禮上仿野,老公的妹妹穿的比我還像新娘。我一直安慰自己她君,他們只是感情好脚作,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般球涛。 火紅的嫁衣襯著肌膚如雪劣针。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,842評(píng)論 1 290
  • 那天亿扁,我揣著相機(jī)與錄音捺典,去河邊找鬼。 笑死从祝,一個(gè)胖子當(dāng)著我的面吹牛襟己,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播牍陌,決...
    沈念sama閱讀 38,997評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼稀蟋,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了呐赡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,741評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤骏融,失蹤者是張志新(化名)和其女友劉穎链嘀,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體档玻,經(jīng)...
    沈念sama閱讀 44,203評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡怀泊,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了误趴。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霹琼。...
    茶點(diǎn)故事閱讀 38,673評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凉当,靈堂內(nèi)的尸體忽然破棺而出枣申,到底是詐尸還是另有隱情,我是刑警寧澤看杭,帶...
    沈念sama閱讀 34,339評(píng)論 4 330
  • 正文 年R本政府宣布忠藤,位于F島的核電站,受9級(jí)特大地震影響楼雹,放射性物質(zhì)發(fā)生泄漏模孩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評(píng)論 3 313
  • 文/蒙蒙 一贮缅、第九天 我趴在偏房一處隱蔽的房頂上張望榨咐。 院中可真熱鬧,春花似錦谴供、人聲如沸块茁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)龟劲。三九已至胃夏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間昌跌,已是汗流浹背仰禀。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚕愤,地道東北人答恶。 一個(gè)月前我還...
    沈念sama閱讀 46,394評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像萍诱,于是被迫代替她去往敵國(guó)和親悬嗓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評(píng)論 2 349

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法裕坊,類相關(guān)的語(yǔ)法包竹,內(nèi)部類的語(yǔ)法,繼承相關(guān)的語(yǔ)法籍凝,異常的語(yǔ)法周瞎,線程的語(yǔ)...
    子非魚_t_閱讀 31,598評(píng)論 18 399
  • 1. 實(shí)現(xiàn)單例模式 餓漢模式和懶漢模式單例模式根據(jù)實(shí)例化時(shí)機(jī)分為餓漢模式和懶漢模式。餓漢模式饵蒂,是指不等到單例真正使...
    aaron1993閱讀 221評(píng)論 0 0
  • java 單例模式指整個(gè)程序中只有一個(gè)某個(gè)類的實(shí)例声诸,通常被用來(lái)代表那些本質(zhì)上唯一的系統(tǒng)組件,比如窗口管理器或者文件...
    dcme閱讀 1,042評(píng)論 0 10
  • 前言 本文主要參考 那些年退盯,我們一起寫過(guò)的“單例模式”彼乌。 何為單例模式? 顧名思義渊迁,單例模式就是保證一個(gè)類僅有一個(gè)...
    tandeneck閱讀 2,490評(píng)論 1 8
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理慰照,服務(wù)發(fā)現(xiàn),斷路器琉朽,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139