創(chuàng)建型模式——單例模式

單例模式簡(jiǎn)介

單例模式是java創(chuàng)建型模式之一,主要作用是創(chuàng)建唯一對(duì)象舵匾。


單例模式特點(diǎn):
1.單例類只有一個(gè)實(shí)例飒炎。
2.單例類必須自己創(chuàng)建自己的唯一實(shí)例诡曙,即私有化構(gòu)造方法臀叙。
3.單例類必須給其他對(duì)象提供這一唯一實(shí)例。

單例常見實(shí)現(xiàn)

餓漢式

public class Singleton {
    private static Singleton instance = new Singleton();

    private Singleton() {

    }

    public static Singleton getInstance() {
        return instance;
    }
}

餓漢式單例模式在單例類加載的時(shí)候就創(chuàng)建了單例對(duì)象岗仑,由于類加載有JVM控制執(zhí)行匹耕,其過程是線程安全的,所以餓漢式是線程安全的荠雕。
特點(diǎn):
1.線程安全
2.空間換取時(shí)間

懶漢式

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

懶漢式單例模式在單例類加載的時(shí)候并沒有實(shí)例化單例對(duì)象,而是在獲取單例類唯一對(duì)象的方法中驶赏,先判斷單例對(duì)象是否為空炸卑,為空的時(shí)候?qū)嵗瘑卫龑?duì)象,然后將該對(duì)象返回給其他對(duì)象煤傍。由于第一次獲取時(shí)單例對(duì)象必定為空盖文,所以第一獲取時(shí)會(huì)有實(shí)例化對(duì)象的過程,執(zhí)行速度會(huì)比之后獲取時(shí)長(zhǎng)蚯姆。
特點(diǎn):
1.線程不安全(后面分析)
2.時(shí)間換取空間

線程安全下的單例模式

為什么懶漢式不是線程安全的五续?

如果有多個(gè)線程同時(shí)第一次調(diào)用getInstance方法,在第一個(gè)線程判斷instance為空進(jìn)入if語(yǔ)句塊準(zhǔn)備執(zhí)行 new Singleton()時(shí)龄恋,第二個(gè)線程也進(jìn)入了getInstance方法疙驾,由于第一個(gè)線程還未執(zhí)行完new Singleton()方法,此時(shí)instance對(duì)象為空郭毕,從而使第二個(gè)線程也進(jìn)入了if語(yǔ)句塊它碎。同樣的情況可能發(fā)生在N個(gè)線程中,從而instance可能被初始化N次。這樣就失去了單例的唯一性扳肛。

synchronized同步getInstance實(shí)現(xiàn)線程安全傻挂。

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

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

將getInstance整個(gè)方法用sychronized關(guān)鍵字修飾,這樣在多線程訪問getInstance的時(shí)候同時(shí)進(jìn)入方法進(jìn)行判斷的只有一個(gè)線程挖息,這樣就可以避免多次實(shí)例化單例對(duì)象金拒。但是這樣鎖粒度太大了,導(dǎo)致多線程獲取實(shí)例化對(duì)象的效率大大降低套腹。(注釋部分同樣能保證線程安全殖蚕,但同樣所鎖密度太大)

DCL(Double Check Lock)

public class Singleton {
    private static Singleton instance = null;

    private Singleton() {

    }

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

//    public static Singleton getInstance(){
//        if (instance == null)
              //多個(gè)線程進(jìn)入此處,雖然后面的線程會(huì)等待鎖施放沉迹,
              //但是同時(shí)也能進(jìn)入同步代碼塊睦疫,導(dǎo)致實(shí)例化多個(gè)單例對(duì)象,從而線程不安全鞭呕。
//            synchronized (Singleton.class) {
//                instance = new Singleton();
//            }
//        }
//        return instance;
//    }
}

分析注釋部分代碼蛤育,最后出現(xiàn)了DCL雙重鎖單例模式。多個(gè)線程雖然可以進(jìn)入第一個(gè)instance==null的if語(yǔ)句塊葫松,但是由于后續(xù)的同步代碼塊中又判斷了一次instance == null瓦糕,所以并不會(huì)實(shí)例化多個(gè)單例對(duì)象。從而保證了線程安全腋么。

DCL的隱患

DCL看似完美咕娄,但是依然存在隱患,而這個(gè)隱患就在instance = new Singleton()這個(gè)句代碼上珊擂。

在代碼看來只有一句圣勒,但是jvm在執(zhí)行這句語(yǔ)句的時(shí)候會(huì)有3個(gè)步驟
1.在java堆上分配一塊內(nèi)存M
2.在M上執(zhí)行實(shí)例化單例對(duì)象
3.將M地址指向instance

以上順序是我們需要的順序,然而JVM在執(zhí)行的時(shí)候會(huì)進(jìn)行指令重排和優(yōu)化摧扇,優(yōu)化后的執(zhí)行順序會(huì)成為:
1.在java堆上分配一塊內(nèi)存M
2.將M地址指向instance
3.在M上實(shí)例化單例對(duì)象

在多線程中圣贸,如果jvm按照優(yōu)化過后的順序執(zhí)行到2的時(shí)候,其他線程調(diào)用了getInstance()方法扛稽,此時(shí)在第一個(gè)判斷instance == null時(shí)會(huì)返回false吁峻,從而繞過同步代碼塊,直接返回instance對(duì)象引用在张,然而此時(shí)instance對(duì)象并沒有實(shí)例化完成用含,從而在其他線程調(diào)用的instance時(shí)發(fā)生NPE。
解決方法:此處的問題涉及到j(luò)ava并發(fā)編程中的有序性帮匾,使用volatile關(guān)鍵字修飾instance即可啄骇。

java并發(fā)編程之原子性,可見性辟狈,有序性(還沒寫^_^)

其他單例模式實(shí)現(xiàn)

1.靜態(tài)內(nèi)部類實(shí)現(xiàn)單例

public class Singleton {

    private Singleton() {

    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
}

特點(diǎn):延遲加載肠缔,線程安全

2.枚舉實(shí)現(xiàn)單例

public enum EnumSingleton {
    /**
     * 單例對(duì)象
     */
    INSTANCE;
    public EnumSingleton getInstance(){
        return INSTANCE;
    }
}

特點(diǎn):防止反射創(chuàng)建多個(gè)單例對(duì)象

3.使用容器實(shí)現(xiàn)單例

public class SingletonManager {
    private static Map<String, Object> singletonMap = new HashMap<>();

    private SingletonManager() {
    }

    public static void registerService(String key, Object singleton) {
        if (singletonMap.containsKey(key)) {
            throw new RuntimeException("重復(fù)注冊(cè)");
        } else {
            singletonMap.put(key, singleton);
        }
    }

    public static Object getService(String key) {
        if (singletonMap.containsKey(key)) {
            return singletonMap.get(key);
        } else {
            return null;
        }
    }
}

特點(diǎn):方便管理夏跷,android中的服務(wù)使用此方式。

Kotlin單例object實(shí)現(xiàn)

在使用Kotlin語(yǔ)言時(shí)明未,實(shí)現(xiàn)單例是非常簡(jiǎn)單的

object Singleton{

}

通過kotlinc將Singleton的kt文件編譯成class文件:

public final class Singleton{
    public static final SingletonINSTANCE;

    private Singleton() {
    }

    static {
        Singleton var0 = new Singleton();
        INSTANCE = var0;
    }
}

koltin中的object單例使用的是餓漢式實(shí)現(xiàn)的槽华,所以是線程安全的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末趟妥,一起剝皮案震驚了整個(gè)濱河市猫态,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌披摄,老刑警劉巖亲雪,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異疚膊,居然都是意外死亡义辕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門寓盗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灌砖,“玉大人,你說我怎么就攤上這事傀蚌』裕” “怎么了?”我有些...
    開封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵善炫,是天一觀的道長(zhǎng)撩幽。 經(jīng)常有香客問我,道長(zhǎng)箩艺,這世上最難降的妖魔是什么窜醉? 我笑而不...
    開封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮舅桩,結(jié)果婚禮上酱虎,老公的妹妹穿的比我還像新娘。我一直安慰自己擂涛,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開白布聊记。 她就那樣靜靜地躺著撒妈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪排监。 梳的紋絲不亂的頭發(fā)上狰右,一...
    開封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音舆床,去河邊找鬼棋蚌。 笑死嫁佳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的谷暮。 我是一名探鬼主播蒿往,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼湿弦!你這毒婦竟也來了瓤漏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤颊埃,失蹤者是張志新(化名)和其女友劉穎蔬充,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體班利,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饥漫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了罗标。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片庸队。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖馒稍,靈堂內(nèi)的尸體忽然破棺而出皿哨,到底是詐尸還是另有隱情,我是刑警寧澤纽谒,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布证膨,位于F島的核電站,受9級(jí)特大地震影響鼓黔,放射性物質(zhì)發(fā)生泄漏央勒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一澳化、第九天 我趴在偏房一處隱蔽的房頂上張望崔步。 院中可真熱鬧,春花似錦缎谷、人聲如沸井濒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)瑞你。三九已至,卻和暖如春希痴,著一層夾襖步出監(jiān)牢的瞬間者甲,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工砌创, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留虏缸,地道東北人鲫懒。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像刽辙,于是被迫代替她去往敵國(guó)和親窥岩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

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