• <menu id="gyiem"><menu id="gyiem"></menu></menu>
  • <menu id="gyiem"><code id="gyiem"></code></menu>

    Java設計模式(十) 你真的用對單例模式了嗎?

    原創文章,轉載請務必將下面這段話置于文章開頭處(保留超鏈接)。
    本文轉發自技術世界原文鏈接 http://www.luozeyang.com/design_pattern/singleton/

    為何需要單例模式

    對于系統中的某些類來說,只有一個實例很重要,例如,一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。

    單例模式設計要點

    • 保證該類只有一個實例。將該類的構造方法定義為私有方法,這樣其他處的代碼就無法通過調用該類的構造方法來實例化該類的對象
    • 提供一個該實例的訪問點。一般由該類自己負責創建實例,并提供一個靜態方法作為該實例的訪問點

    餓漢 vs. 懶漢

    • 餓漢 聲明實例引用時即實例化
    • 懶漢 靜態方法第一次被調用前不實例化,也即懶加載。對于創建實例代價大,且不定會使用時,使用懶加載模式可以減少開銷

    實現單例模式的九種方法

    線程不安全的懶漢 - 多線程不可用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    package com.jasongj.singleton1;

    public class Singleton {

    private static Singleton INSTANCE;

    private Singleton() {};

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

    }
    • 優點:達到了Lazy Loading的效果
    • 缺點:只有在單線程下能保證只有一個實例,多線程下有創建多個實例的風險

    同步方法下的懶漢 - 可用,不推薦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.jasongj.singleton2;

    public class Singleton {

    private static Singleton INSTANCE;

    private Singleton() {};

    public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
    INSTANCE = new Singleton();
    }
    return INSTANCE;
    }
    }
    • 優點:線程安全,可確保正常使用下(不考慮通過反射調用私有構造方法)只有一個實例
    • 缺點:每次獲取實例都需要申請鎖,開銷大,效率低

    同步代碼塊下的懶漢 - 不可用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.jasongj.singleton3;

    public class Singleton {

    private static Singleton INSTANCE;

    private Singleton() {};

    public static Singleton getInstance() {
    if (INSTANCE == null) {
    synchronized (Singleton.class) {
    INSTANCE = new Singleton();
    }
    }
    return INSTANCE;
    }
    }
    • 優點:不需要在每次調用時加鎖,效率比上一個高
    • 缺點:雖然使用了synchronized,但本質上是線程不安全的。

    雙重檢查(Double Check)下的懶漢 - 推薦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package com.jasongj.singleton4;

    public class Singleton {

    private static volatile Singleton INSTANCE;

    private Singleton() {};

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

    }
    • 優點:使用了雙重檢查,避免了線程不安全,同時也避免了不必要的鎖開銷。
    • 缺點:NA

    注:

    • 但是這里的synchronized已經保證了INSTANCE寫操作對其它線程讀操作的可見性。具體原理請參考《Java進階(二)當我們說線程安全時,到底在說什么
    • 使用volatile關鍵字的目的不是保證可見性(synchronized已經保證了可見性),而是為了保證順序性。具體來說,INSTANCE = new Singleton()不是原子操作,實際上被拆分為了三步:1) 分配內存;2) 初始化對象;3) 將INSTANCE指向分配的對象內存地址。 如果沒有volatile,可能會發生指令重排,使得INSTANCE先指向內存地址,而對象尚未初始化,其它線程直接使用INSTANCE引用進行對象操作時出錯。詳細原理可參見《雙重檢查鎖定與延遲初始化

    靜態常量 餓漢 - 推薦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    package com.jasongj.singleton6;

    public class Singleton {

    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {};

    public static Singleton getInstance() {
    return INSTANCE;
    }

    }
    • 優點:實現簡單,無線程同步問題
    • 缺點:在類裝載時完成實例化。若該實例一直未被使用,則會造成資源浪費

    靜態代碼塊 餓漢 可用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    package com.jasongj.singleton7;

    public class Singleton {

    private static Singleton INSTANCE;

    static{
    INSTANCE = new Singleton();
    }

    private Singleton() {};

    public static Singleton getInstance() {
    return INSTANCE;
    }

    }
    • 優點:無線程同步問題
    • 缺點:類裝載時創建實例,無Lazy Loading。實例一直未被使用時,會浪費資源

    靜態內部類 推薦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package com.jasongj.singleton8;

    public class Singleton {

    private Singleton() {};

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

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

    }
    • 優點:無線程同步問題,實現了懶加載(Lazy Loading)。因為只有調用getInstance時才會裝載內部類,才會創建實例。同時因為使用內部類時,先調用內部類的線程會獲得類初始化鎖,從而保證內部類的初始化(包括實例化它所引用的外部類對象)線程安全。即使內部類創建外部類的實例Singleton INSTANCE = new Singleton()發生指令重排也不會引起雙重檢查(Double-Check)下的懶漢模式中提到的問題,因此無須使用volatile關鍵字。
    • 缺點:NA

    枚舉 強烈推薦

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package com.jasongj.singleton9;

    public enum Singleton {

    INSTANCE;

    public void whatSoEverMethod() { }

    // 該方法非必須,只是為了保證與其它方案一樣使用靜態方法得到實例
    public static Singleton getInstance() {
    return INSTANCE;
    }

    }
    • 優點:枚舉本身是線程安全的,且能防止通過反射和反序列化創建多實例。
    • 缺點:使用的是枚舉,而非類。   

    Java設計模式系列

    郭俊 Jason wechat
    歡迎關注作者微信公眾號【大數據架構】
    您的贊賞將支持作者繼續原創分享
    速赢彩app 禹州 | 海丰 | 红河 | 安阳 | 海南 | 舟山 | 滁州 | 安阳 | 玉树 | 甘肃兰州 | 巴彦淖尔市 | 大同 | 阳春 | 眉山 | 松原 | 绥化 | 徐州 | 平凉 | 基隆 | 宜都 | 武威 | 荆州 | 五家渠 | 宿迁 | 金坛 | 鸡西 | 驻马店 | 金华 | 武夷山 | 衢州 | 果洛 | 长兴 | 莆田 | 灌南 | 江门 | 台州 | 通化 | 揭阳 | 沭阳 | 郴州 | 烟台 | 青州 | 自贡 | 清徐 | 玉溪 | 岳阳 | 安康 | 昌吉 | 黑龙江哈尔滨 | 涿州 | 临海 | 河北石家庄 | 龙岩 | 鄂州 | 克孜勒苏 | 基隆 | 昭通 | 张家口 | 昆山 | 温州 | 五家渠 | 醴陵 | 博尔塔拉 | 姜堰 | 塔城 | 信阳 | 潍坊 | 塔城 | 南京 | 孝感 | 海拉尔 | 海南 | 湖州 | 济宁 | 涿州 | 禹州 | 桐城 | 章丘 | 台中 | 芜湖 | 吉林 | 齐齐哈尔 | 湛江 | 青海西宁 | 陵水 | 吐鲁番 | 雅安 | 新余 | 汕头 | 崇左 | 湘西 | 株洲 | 开封 | 垦利 | 白山 | 醴陵 | 嘉兴 | 韶关 | 盘锦 | 乌兰察布 | 南安 | 丹东 | 鄢陵 | 湖州 | 运城 | 克拉玛依 | 三门峡 | 屯昌 | 湖北武汉 | 广饶 | 文山 | 定州 | 阿拉尔 |