單例模式

單例的定義

確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并且向整個(gè)系統(tǒng)提供整個(gè)實(shí)例紊撕。

使用場(chǎng)景

確保一個(gè)類有且只有一個(gè)對(duì)象,避免產(chǎn)生多個(gè)對(duì)象消耗資源憨募,比如訪問IO和數(shù)據(jù)庫(kù)等資源的時(shí)候就需要使用單例

實(shí)現(xiàn)方式

最簡(jiǎn)單的實(shí)現(xiàn)紧索,線程不安全

保證只有一個(gè)對(duì)象,那么構(gòu)造方法肯定得是私有的菜谣,通過(guò)一個(gè)靜態(tài)方法獲取對(duì)象珠漂,當(dāng)對(duì)象為空的時(shí)候初始化對(duì)象晚缩,否則直接返回,需要用到的時(shí)候再去初始化媳危,屬于懶加載模式荞彼。

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

缺點(diǎn)就是當(dāng)多線程調(diào)用getInstance的時(shí)候可能會(huì)初始化多個(gè)對(duì)象,所以這個(gè)方法不可取

懶漢式待笑,線程安全

那么可以加上有個(gè)同步來(lái)試試

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

其實(shí)這樣是OK的鸣皂,當(dāng)執(zhí)行方法的時(shí)候,在同一時(shí)刻只能有一個(gè)線程得到執(zhí)行暮蹂,另一個(gè)線程受阻塞寞缝,必須等待當(dāng)前線程執(zhí)行完這個(gè)代碼塊以后才能執(zhí)行該代碼塊;但是有個(gè)小問題仰泻,這樣效率比較低荆陆;所以可以稍微優(yōu)化一下,把同步放到了判斷為空以后

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

這個(gè)樣子看起來(lái)是可以的集侯,但是還是有一個(gè)小問題被啼,不容易被發(fā)現(xiàn);就是如果一個(gè)線程執(zhí)行到singleton = new Singleton()這一步的時(shí)候(不屬于原子操作)棠枉,這句代碼包括了三步浓体,

  • singleton分配內(nèi)存,
  • 調(diào)用 Singleton 的構(gòu)造函數(shù)來(lái)初始化成員變量术健,
  • 將instance對(duì)象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)

在 JVM 的即時(shí)編譯器中存在指令重排序的優(yōu)化汹碱,上面的第二步和第三步的順序是不能保證的,最終的執(zhí)行順序可能是 1-2-3 也可能是 1-3-2荞估,所以如果直接執(zhí)行了1-3-2咳促,這時(shí)候剛好另一個(gè)線程執(zhí)行進(jìn)來(lái),執(zhí)行if (singleton == null)代碼勘伺,判斷不為空然后拿著對(duì)象去使用跪腹,但其實(shí)變量還沒有初始化,程序就會(huì)出錯(cuò)飞醉;

雙重檢驗(yàn)鎖模式

我們可以將singleton變量聲明成volatile冲茸,它有一個(gè)特性就是禁止指令重排序優(yōu)化,這樣就沒有問題了缅帘;

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

餓漢式 static final field

但是有沒有發(fā)現(xiàn)上面這種方法寫起來(lái)有點(diǎn)麻煩轴术;所以簡(jiǎn)單點(diǎn),可以直接使用靜態(tài)變量

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

這種寫法其實(shí)是OK的钦无,可以使用的逗栽,缺點(diǎn)在于不是一種懶加載模式,加載類后就被初始化失暂,即使可能用不到彼宠;而且在有些場(chǎng)景鳄虱,比如需要傳入?yún)?shù)時(shí)候,就不適用了

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

我比較喜歡這種方法凭峡,感覺是比較好的拙已,寫起來(lái)也比較方便,這種方法也是《Effective Java》上所推薦的

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

枚舉 Enum

枚舉也是一種很好的方法摧冀,很簡(jiǎn)單倍踪,而且還能防止反序列化導(dǎo)致重新創(chuàng)建新的對(duì)象;但是好像一般也沒什么人用按价,我覺得還OK

public enum  Singleton {
    SINGLETON;
    public static void main(String[] args){
        Singleton  singleton=Singleton.SINGLETON;
    }
}

使用容器實(shí)現(xiàn)單例模式

可以在程序初始化的時(shí)候?qū)⒏鞣N單例類型注入到一個(gè)統(tǒng)一的管理類中惭适,根據(jù)key值來(lái)獲取相應(yīng)的對(duì)象,這也是一種方法

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

    private SingletonManager() {
    }

    /**
     * 注入單例
     *
     * @param key
     * @param instance
     */
    public static void registerService(String key, Object instance) {
        if (!objectMap.containsKey(key)) {
            objectMap.put(key, instance);
        }
    }

    /**
     * 獲取單例
     *
     * @param key
     * @return
     */
    public static Object getService(String key) {
        return objectMap.get(key);
    }

}

總結(jié)

大概也就這幾種常見的單例寫法楼镐,其實(shí)就是為了確保一個(gè)類有且只有一個(gè)對(duì)象癞志,自己覺得哪種合適,哪種方便就使用哪種好了框产;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凄杯,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子秉宿,更是在濱河造成了極大的恐慌戒突,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件描睦,死亡現(xiàn)場(chǎng)離奇詭異膊存,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)忱叭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門隔崎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人韵丑,你說(shuō)我怎么就攤上這事爵卒。” “怎么了撵彻?”我有些...
    開封第一講書人閱讀 164,133評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵钓株,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我陌僵,道長(zhǎng)轴合,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評(píng)論 1 293
  • 正文 為了忘掉前任碗短,我火速辦了婚禮值桩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好捆探,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評(píng)論 6 392
  • 文/花漫 我一把揭開白布共耍。 她就那樣靜靜地躺著,像睡著了一般窄潭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,462評(píng)論 1 302
  • 那天澜建,我揣著相機(jī)與錄音,去河邊找鬼蝌以。 笑死炕舵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跟畅。 我是一名探鬼主播咽筋,決...
    沈念sama閱讀 40,262評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼徊件!你這毒婦竟也來(lái)了奸攻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤虱痕,失蹤者是張志新(化名)和其女友劉穎睹耐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體部翘,經(jīng)...
    沈念sama閱讀 45,587評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡硝训,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了新思。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窖梁。...
    茶點(diǎn)故事閱讀 39,919評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖表牢,靈堂內(nèi)的尸體忽然破棺而出窄绒,到底是詐尸還是另有隱情,我是刑警寧澤崔兴,帶...
    沈念sama閱讀 35,635評(píng)論 5 345
  • 正文 年R本政府宣布彰导,位于F島的核電站,受9級(jí)特大地震影響敲茄,放射性物質(zhì)發(fā)生泄漏位谋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評(píng)論 3 329
  • 文/蒙蒙 一堰燎、第九天 我趴在偏房一處隱蔽的房頂上張望掏父。 院中可真熱鬧,春花似錦秆剪、人聲如沸赊淑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)陶缺。三九已至钾挟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饱岸,已是汗流浹背掺出。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留苫费,地道東北人汤锨。 一個(gè)月前我還...
    沈念sama閱讀 48,048評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像百框,于是被迫代替她去往敵國(guó)和親闲礼。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評(píng)論 2 354

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