單例模式

Singleton單例模式簡介

在應(yīng)用單例模式時,單例對象的類必須保證只有一個實(shí)例存在.許多時候整個系統(tǒng)只需要一個全局對象, 這樣有利于協(xié)調(diào)系統(tǒng)整體行為,如在一個應(yīng)用中,應(yīng)該只有ImageLoader實(shí)例,這個ImageLoader中又含有線程池,緩存系統(tǒng),網(wǎng)絡(luò)請求等, 很消耗資源,所以不能多次構(gòu)建實(shí)例.

構(gòu)建單例模式要保證幾點(diǎn):

  • 構(gòu)造方法不對外開放,一般為private
  • 通過一個靜態(tài)方法或者枚舉返回單例類對象
  • 確保單例對象有且只有一個,尤其在多線程的環(huán)境下.
  • 確保單例對象在反序列化時不會重新構(gòu)建對象.

構(gòu)建單例模式的方式:

  • 懶漢式: 指全局的單例實(shí)例在第一次被使用時構(gòu)建
  • 餓漢式: 指全局的單例實(shí)例在類轉(zhuǎn)載時構(gòu)建.

懶漢式單例

單線程的懶漢式單例:

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

這種最簡單的懶漢式單例只有在單線程中才有作用, 如果在多線程中由于多線程執(zhí)行的問題的會因?yàn)榫€程并發(fā)的問題產(chǎn)生多個實(shí)例.所以我們需要同步即:

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

但這種線程同步還是不能做到線程安全的問題, 還是會產(chǎn)生多個實(shí)例對象, 所以我們再一次進(jìn)行判斷nul:

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

這種雙重判斷DCL單例在JDK小于1.5時還是會為因?yàn)闃?gòu)造對象出現(xiàn)指令重排序的問題, 故而給單例對象添加volatile關(guān)鍵字修飾, 禁止指令重排序; 所以最終版為:


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

從上面代碼中可以看到:

  • 構(gòu)造方法私有化;
  • 單例對象使用volatile修飾;
  • 使用靜態(tài)方法getInstance()返回單例對象;
  • 在構(gòu)建單例對象使用Double-CheckLock(DCL);

使用DLC雙重檢查,第一次判斷sInstance=null,是為了避免不必要的同步問題,第二次判斷sInstance=null是為了避免多次創(chuàng)建實(shí)例對象.其實(shí)在構(gòu)建實(shí)例sInstance=new Singleton()時, 這句代碼并不是一個原子操作,可以分為三步:

  • 給Singleton實(shí)例分配內(nèi)存
  • 調(diào)用Singleton()構(gòu)造函數(shù),初始化成員變量字段
  • 將sInstance對象指向分配的內(nèi)存空間.

但是由于指令重排序的原因,可能不保證上述三點(diǎn)不按照順序執(zhí)行,可能是1-2-3,也可能是2-1-3或者1-3-2,如果是第三點(diǎn)先執(zhí)行,第二點(diǎn)還未執(zhí)行,就切換到其他線程,sInstance已經(jīng)非空,就不會構(gòu)建實(shí)例,使用時就會崩潰.

知道是指令重排序造成的問題后,只要禁止指令重排序既可, 就可以使用volatile關(guān)鍵字修飾sInstance,保證單例對象內(nèi)存可見性.

餓漢式單例


    public class Singleton{
        
        private static final Singleton sInstance= new Singleton();
        
        private Singleton(){};
        
        public static Singleton getInstance(){
            return sInstance;
        }
        
    }

餓漢式單例存在的特點(diǎn)也很明顯: 由于sInstance實(shí)例在類加載時進(jìn)行的,而類的加載是由ClassLoader來進(jìn)行,故而實(shí)例的初始化時機(jī)比較難把握,可能由于初始化時機(jī)太早,造成資源浪費(fèi),如果初始化本身依賴一些其他數(shù)據(jù),那么很難保證其他數(shù)據(jù)在這之前已經(jīng)準(zhǔn)備就緒.

那么什么時候會類加載? 不太嚴(yán)格的說,類的加載一般會出現(xiàn)在一下幾個時機(jī):

  • new一個對象是;
  • 使用反射創(chuàng)建實(shí)例時;
  • 子類被加載時,如果父類還未加載,就先加載父類
  • JVM啟動執(zhí)行的主類會首先被加載.

靜態(tài)內(nèi)部類單例模式

DCL雙重檢查單例模式雖然在一定程度上解決了資源消耗,多余的同步,線程安全問題,但是他還是會出現(xiàn)失效問題, 這種問題被稱為雙重檢查鎖定(DCL)失效,建議使用如下代碼:


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

當(dāng)?shù)谝淮渭虞dSingleton類并不會初始化sInstance,只有在第一次調(diào)用Singleton的getInstance()才會初始化sInstance.

枚舉單例

    
    public enum SingletonEnum{
        INSTANCE;
        public void doSomething(){
            //...
        }
    }

枚舉單例的最大優(yōu)勢在于,無償提供了序列化機(jī)制,絕對防止對象實(shí)例化,即使在面對復(fù)雜的序列化或者反射攻擊的時候.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末颗胡,一起剝皮案震驚了整個濱河市氢烘,隨后出現(xiàn)的幾起案子泛释,更是在濱河造成了極大的恐慌伍宦,老刑警劉巖儒鹿,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件着倾,死亡現(xiàn)場離奇詭異戒职,居然都是意外死亡缘屹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門噪猾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霉祸,“玉大人,你說我怎么就攤上這事袱蜡∷坎洌” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵坪蚁,是天一觀的道長奔穿。 經(jīng)常有香客問我,道長敏晤,這世上最難降的妖魔是什么贱田? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮嘴脾,結(jié)果婚禮上男摧,老公的妹妹穿的比我還像新娘。我一直安慰自己译打,他們只是感情好耗拓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奏司,像睡著了一般乔询。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上韵洋,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天竿刁,我揣著相機(jī)與錄音岸夯,去河邊找鬼。 笑死们妥,一個胖子當(dāng)著我的面吹牛猜扮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播监婶,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼旅赢,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了惑惶?” 一聲冷哼從身側(cè)響起煮盼,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎带污,沒想到半個月后僵控,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鱼冀,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年报破,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片千绪。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡充易,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荸型,到底是詐尸還是另有隱情盹靴,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響州刽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜改备,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柳琢。 院中可真熱鬧绍妨,春花似錦润脸、人聲如沸柬脸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽倒堕。三九已至,卻和暖如春爆价,著一層夾襖步出監(jiān)牢的瞬間垦巴,已是汗流浹背媳搪。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骤宣,地道東北人秦爆。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像憔披,于是被迫代替她去往敵國和親等限。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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