设计模式私家笔记-单例模式Singleton

场景:有的情况下,我们只需要创建一个实例,以此来节省创建和销毁的消耗,以及保持该实例的稳定性(不希望被外部破坏或篡改)

此时我们可以应用单例模式来创建该实例的对象,下面列举几种方法,并且说明其各自的使用场景和优缺点:

饿汉模式

顾名思义,我很饥饿,我上来就创建该实例,不惯用不用得着,并且以后都使用该实例。示例代码:

class Mgr {
    private static final INSTANCE = new Mgr();
    private Mgr() {}
    public static Mgr getInstance() {
        return INSTANCE;
    }
}

关键代码说明:

  1. 构造器是私有的,这样就避免了被外部实例化
  2. 属性加上final属性之后,也可以避免被再次赋值,而像通过下面的静态代码块来初始化,就不能加上final修饰符来限制
// 省略
private static INSTANCE;
static {
    INSTANCE = new Mgr();
}
// 省略

懒汉模式 - 双重锁

饿汉模式比较简单,但是有的时候实例化一次资源如果消耗很大的话,我们就希望在对象被实际使用到(调用)的时候才进行实例化,我们很容易想到下面的代码实现:

class Mgr {
    private static INSTANCE;
    private Mgr() {}
    public static Mgr getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new Mgr();
        }
        
        return INSTANCE;
    }
}

看起来似乎不错,但在并发多线程场景下,进行对象实例化的代码INSTANCE = new Mgr();是有可能被同时执行到的,这样一来,就不满足单例的要求了。
怎么办呢?熟悉多线程的工程师会给这个实例化方法加上一个锁来解决这个问题,像下面这样:

// 省略
    public static syncronized Mgr getInstance() {
        if(INSTANCE == null) {
            INSTANCE = new Mgr();
        }
        
        return INSTANCE;
    }
// 省略

开启多个线程测试也能通过,但仍然有缺陷,因为加锁就以为着,这个方法被所有线程一个一个的排队去使用,多线程失去了意义,性能损失很大。

接着往下想,怎么办?能不能在需要进行实例化的代码块上进行精准加锁呢?像下面这样

// 省略
    public static syncronized Mgr getInstance() {
        if(INSTANCE == null) {
            syncronized(Mgr.class) {
                INSTANCE = new Mgr();
            }
        }
        
        return INSTANCE;
    }
// 省略

这样是不是就完美了?实际还有问题的,如果多个线程同时执行了代码if(INSTANCE == null)并且都等待锁去做对象实例化,就还会new出来多个实例。于是,下面就引出了经典的双重锁方式

// 省略
    public static syncronized Mgr getInstance() {
        if(INSTANCE == null) {
            syncronized(Mgr.class) {
                if(INSTANCE == null) {
                    INSTANCE = new Mgr();
                }
            }
        }
        
        return INSTANCE;
    }
// 省略

这样一来,即便是有多个线程拿到了锁要去做对象的实例化,还会再次检查一下对象是否为空才会执行实例化逻辑。因为第一个拿到锁线程一定已经完成了对象的实例化,其他拿到锁的线程检查if(INSTANCE == null)的时候就一定是false了。

懒汉模式 - 静态内部类

在Effective Java这本书中,作者提到了更为简洁的时间懒汉模式的方法——通过静态内部类。我们将上述代码改造一下

class Mgr {
    private Mgr() {}
    private static class MgrHelpler {
        private static final INSTANCE = new Mgr();
        public static Mgr getInstance() {
            return INSTANCE;
        }
    }

    public Mgr getInstance() {
        return MgrHelpler.getInstance();
    }
}

看起来简洁多了。但是稍微难理解一点,这利用了JVM类加载的机制,在加载类的时候,静态内部类MgrHelpler会被加载,但内部的静态属性 INSTANCE 只有在被使用到的时候,才会被实例化,后续再调用就直接使用该实例了。

懒汉模式 - 枚举属性

还有一种预发层面天然支持,就是枚举,枚举的构造器默认就是private的,因此可以很好的避免外部的篡改,另外枚举的属性只有在使用到时候,才会进行初始化,因而也可以避免使用前的资源消耗。上面的代码可以修改为以下简单的几行

enum Mgr {
    INSTANCE
    Mgr() {}
}

so easy,当然实际场景实例化过程会添加到构造器当中。

总结

上面介绍了4种可行的构建单例的模式:

  • 饿汉模式
  • 懒汉模式 - 双重锁
  • 懒汉模式 - 静态内部类
  • 懒汉模式 - 枚举属性

一般来讲,没有特殊的考虑,使用饿汉模式即可,简单易于理解,如果处于性能考虑,3中懒汉模式可以根据实际情况进行使用。


笔者原文发布在CSDN,欢迎点击查看:https://blog.csdn.net/fuhua_chou/article/details/124553365

也可以关注笔者:请给我一根烟的时间(https://blog.csdn.net/mytream,查看更多个人心得和分享

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容