設(shè)計模式雜談(1)單例模式拧略。【使用頻率:99.999%尚辑,學習難度:0.001%】

作者:solo陳,轉(zhuǎn)載請注明出處盔腔。
個人主頁:http://www.reibang.com/users/5c2177416a84/latest_articles
??設(shè)計模式是你在學習java道路上必須要學會掌握的杠茬,當然也并不是24種設(shè)計模式你都要掌握得很透徹。下面列出幾項:《設(shè)計模式的好處》
一弛随、有助于設(shè)計系統(tǒng)架構(gòu)瓢喉、增強系統(tǒng)健壯性、利于維護
二舀透、有利于加強思考問題的思維栓票、思考能力
三、有利于自己的編寫代碼愕够、閱讀代碼的能力
??當然你在編碼設(shè)計過程中如果只是為了顯擺一下你會某種設(shè)計模式這個是沒必要的走贪,因為過多的使用并不會給程序帶來良好的閱讀性,反而會增加系統(tǒng)的復雜度惑芭,所以應(yīng)該因地制宜坠狡。其實如何在正確的地方正確的使用設(shè)計模式是初學者普遍會問的問題。比如LZ當時就會有這個的問題:學了單例模式遂跟,自己也會寫逃沿,但是在什么情況下使用呢?這個就不得而知了幻锁,導致自己很多時候都很迷茫自己究竟有沒有學習設(shè)計模式的必要凯亮。所以LZ寫這個文章的初衷是將工作中使用到的結(jié)合起來進行說明:
??下面先來說說單例模式。單例模式不論在自己編碼或者源代碼中都會遇見哄尔,所以此模式是你學習時候必須掌握的假消。該在什么時候使用呢?LZ在學習中也常常問自己岭接,單例模式都有一個共性就是
1置谦、這個類沒有狀態(tài)
??有狀態(tài)的類:類里面有成員變量,而且成員變量是可變的亿傅、比如struts2的action媒峡、要求是多例的、因為他是有狀態(tài)的
??無狀態(tài)類:類里面沒有成員變量葵擎,或者有成員變量但是不可變的谅阿、或者成員變量是單例的、比如struts1的action、可以是單例的签餐。因為他是沒有狀態(tài)的
2寓涨、比如:工作上類似你一個AppServer類作為啟動資源類并進行各種初始化工作,那么你的這個類就可以寫出單例氯檐,因為這個類在使用的過程中并不需要每次都進行new來實例對象戒良,我們只需要單獨的一份就可以了。
說白了單例就是你new無數(shù)個其實都是一樣的冠摄,并且在邏輯上如果new多個也會發(fā)生邏輯錯誤糯崎。

在網(wǎng)上該模式又分為懶漢、餓漢等幾種形式河泳。LZ就不進行細分了沃呢,因為LZ并不想在學習單例模式的時候再對文字進行理解記憶。下面就是幾種逐漸進化的單例模式:
一拆挥、這種也是學習設(shè)計模式時老師講解的最初級版本

//這是在不考慮并發(fā)訪問的情況下標準的單例模式的構(gòu)造方式薄霜,這種方式通過幾個地方來限制了我們?nèi)〉降膶嵗俏ㄒ坏摹?public class Singleton {    
  private Singleton(){}    
  private static Singleton singleton ;    
  public static Singleton getInstance (){          
        if(singleton == null){                
              singleton = new Singleton();           
        }          
        return singleton ;      
  }
}

這種寫法是在初學是不考慮并發(fā)情況的構(gòu)造方式,通過幾點來確定獲取唯一的實例:
1纸兔、使用private權(quán)限的構(gòu)造器惰瓜,使得客戶端(使用者)不能夠隨意創(chuàng)建對象
2、使用static關(guān)鍵字來使得屬性所指向的對象在每一個類中都是唯一的
3汉矿、static方法鸵熟,使得客戶端可以直接通過類.方法名調(diào)用。如果沒有static就會使得客戶端無法獲取實例進行調(diào)用
??上面的方法屬于大學畢業(yè)階段的代碼负甸,因為他并沒有考慮到如果在多線程環(huán)境會造成的影響流强,如果多個線程(A\B)同時來訪問getInstance這個方法,那么在if判斷這里A線程判斷為空呻待,然而B線程正好在執(zhí)行singleton = new Singleton(); 創(chuàng)建實例方法打月,但是并沒有真正實例出對象,那么A線程也會繼續(xù)執(zhí)行singleton = new Singleton(); 創(chuàng)建實例方法蚕捉。從而導致會創(chuàng)建多個實例奏篙。

二、有人說第一種是沒考慮多線程那么就加鎖

/*此種加鎖方式會導致運行速度降低迫淹,當一個線程進行訪問的時候秘通,其余所有線程都將掛起等待
*/
public class StupidSynchronizedSingleton {
    private StupidSynchronizedSingleton (){}
    private static StupidSynchronizedSingleton badsingleton ; 

    public synchronized static StupidSynchronizedSingleton getInstance (){
        if(badsingleton == null){
            badsingleton = new StupidSynchronizedSingleton();
        }
        return badsingleton ;
    }
}

由于該方法的做法實在是太愚蠢了,所以LZ給它取名Stupid敛熬。上面的做法是將getInstance ()進行同步來解決多線程問題肺稀,但是當訪問getInstance ()時,其余的線程會進行掛起应民,造成無謂的等待话原,顯然這種等待是沒有必要的夕吻。

三、在第二種方法上適當?shù)男薷木涂梢越鉀Q那種無謂的等待了繁仁。

//這種實現(xiàn)方式實現(xiàn)了雙重鎖機制
/**
 *  首先要明白在JVM創(chuàng)建新的對象時涉馅,主要要經(jīng)過三步。
              1.分配內(nèi)存
              2.初始化構(gòu)造器
              3.將對象指向分配的內(nèi)存的地址
              但是在new實例的時候有可能是先將對象分配給內(nèi)存黄虱,在初始化稚矿。這個時候返回的synsingleton就會出現(xiàn)未知的錯誤
 *
 */

public class SynchronizedSingleton {
    private SynchronizedSingleton (){}
    private static SynchronizedSingleton synsingleton ; 
    public static SynchronizedSingleton getInstance (){
        if(synsingleton == null){//1
            synchronized (SynchronizedSingleton.class) {
                if(synsingleton == null){//2
                    synsingleton = new SynchronizedSingleton();
                }
            }
        }
        return synsingleton ;
    }
}

上面方法相比較第二種方法做的同步就要正確得多了,并沒有在方法上直接進行同步捻浦,而是判斷了變量是否為null之后再進行同步晤揣,否則就直接進行返回,從而減少了在有實例情況下的等待時間默勾。
假設(shè)synsingleton為null(1)碉渡,此時有A/B線程同時執(zhí)行到synchronized塊聚谁,假設(shè)A線程搶占到資源再次執(zhí)行synsingleton為null(2)當判斷為true時母剥,就進行實例化對象,否則返回形导。B之后進入同步塊是同樣环疼。再次執(zhí)行判斷的原因是確保多個線程進入注釋1后代碼的邏輯正確性。從上面代碼中可以看到有兩次判斷是否為null朵耕,這就是所謂的雙重鎖機制炫隶。
上面代碼從表面上看是沒有任何問題的,但是如果你了解JVM創(chuàng)建對象的邏輯步驟你就會發(fā)現(xiàn)上面做法也會出現(xiàn)問題阎曹。具體造成問題的原因看代碼上面的注釋伪阶。所以為了避免我們在創(chuàng)建對象時發(fā)生的此種問題,我們最好是交給JVM進行处嫌。

四栅贴、將創(chuàng)建對象的時機將給JVM加載類的時候進行(類加載這里就不詳細說明了,有時間單獨寫一個我對類加載過程的理解的一篇文章)

/*屬性為static的會在類加載的時候初始化熏迹,所以在初始化進行一半的時候檐薯,別的線程      *是無法使用的,這個是jvm保證的
*/
public class InnerSingleton {
    private InnerSingleton (){};
    public static InnerSingleton getInstance (){
        return SingletonInstance.singleton;
    }
    private static class SingletonInstance {
        private static final InnerSingleton singleton = new InnerSingleton();
    }
}

首先static的成員變量會在類加載的時候進行初始化注暗,所以singleton在代碼調(diào)用之前就已經(jīng)實例化好了坛缕。

五、枚舉量來實現(xiàn)單例模式:
1捆昏、 自由序列化赚楚;
2、 保證只有一個實例(即使使用反射機制也無法多次實例化一個枚舉量)骗卜;
3直晨、 線程安全搀军;

public class EnumSingleton {
    private EnumSingleton() {}
    
    private enum InstanceHolder {
        INSTANCE;
        private EnumSingleton value;

        private InstanceHolder() {
            value = new EnumSingleton();
        }
    }
    
    public static EnumSingleton getInstance() {
        return InstanceHolder.INSTANCE.value;
    }
}

枚舉形式不僅能避免多線程同步問題,而且還能防止反序列化重新創(chuàng)建新的對象勇皇,但是枚舉是1.5版本之后的新特性罩句,所以這種方式很少用到

以上就是java單例模式的各種寫法,當然在實際運用中你可以使用四敛摘、五方法來創(chuàng)建门烂,如果是應(yīng)聘,面試官叫你寫單例模式你可以都寫出來然后講出各自的優(yōu)缺點兄淫,這樣相信你對單例模式的掌握已經(jīng)熟練了屯远。
上面就是LZ對單例模式的理解,感謝各位的收看捕虽。這篇文章也是LZ在簡書上寫的第一篇慨丐,后續(xù)也會繼續(xù)分享自己對Java知識的理解,當然LZ并不是什么大牛泄私,也是在不斷的學習過程中分享自己理解房揭,有什么問題可以在文章下發(fā)留言進行交流。有錯的地方LZ也會改正晌端。謝謝捅暴!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市咧纠,隨后出現(xiàn)的幾起案子蓬痒,更是在濱河造成了極大的恐慌,老刑警劉巖漆羔,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件梧奢,死亡現(xiàn)場離奇詭異,居然都是意外死亡演痒,警方通過查閱死者的電腦和手機亲轨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫡霞,“玉大人瓶埋,你說我怎么就攤上這事≌锘Γ” “怎么了养筒?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長端姚。 經(jīng)常有香客問我晕粪,道長,這世上最難降的妖魔是什么渐裸? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任巫湘,我火速辦了婚禮装悲,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘尚氛。我一直安慰自己诀诊,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布阅嘶。 她就那樣靜靜地躺著属瓣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪讯柔。 梳的紋絲不亂的頭發(fā)上抡蛙,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音魂迄,去河邊找鬼粗截。 笑死,一個胖子當著我的面吹牛捣炬,可吹牛的內(nèi)容都是我干的熊昌。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼遥金,長吁一口氣:“原來是場噩夢啊……” “哼浴捆!你這毒婦竟也來了蒜田?” 一聲冷哼從身側(cè)響起稿械,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎冲粤,沒想到半個月后美莫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡梯捕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年厢呵,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片傀顾。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡襟铭,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出短曾,到底是詐尸還是另有隱情寒砖,我是刑警寧澤,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布嫉拐,位于F島的核電站哩都,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏婉徘。R本人自食惡果不足惜漠嵌,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一咐汞、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧儒鹿,春花似錦化撕、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至章钾,卻和暖如春墙贱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背贱傀。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工惨撇, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人府寒。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓魁衙,卻偏偏與公主長得像,于是被迫代替她去往敵國和親株搔。 傳聞我的和親對象是個殘疾皇子剖淀,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

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