单例模式--我,我,我,还是我

作者: space0o0 | 来源:发表于2019-06-12 17:48 被阅读4次

小熊最近997加班写代码,身体严重透支。
没办法,转行去卖房子了。

1·懒汉模式

客户打电话给门店找小熊看房,同事就让客户等一下,他去叫小熊。

public class Singleton{
    private static Singleton singleton;
    
    private Singleton() {
    }

    public synchronized static Single newInstance() {
        if (singleton== null) {//小熊在不在
            singleton= new Singleton();//不在就把他找出来
        }
        return singleton;
    }
}

客户打电话就是一个线程,synchronized就是请客户等一下,避免还有其他客户找小熊乱了。

这就是最常见的单例模式,通过newInstance()来延迟加载,synchronized避免多线程问题。

2·饿汉模式

小熊早上9点到了门店。
同事说:有个客户9点30分要找你,你就在这等着吧!
小熊是新人呢,没办法,就等啊等,等得都没时间去吃早饭,就变成了饿熊~

public class Singleton {
    //小熊就提前等着,不管有没有人叫他
    private static Singleton singleton= new Singleton();
    
    private singleton(){}

    public static Singleton newInstance() {
    //客户来电话了,小熊出去带客户看房了,可是没吃早饭好饿呀!
        return singleton;
    }
}

在类加载的时候,最初就声明了对象,避免了多线程问题,可是考虑对象初始化的复杂程度所需要消耗的时间和没必要的内存开销,这么写也太随便了。

3·双重检查锁(懒汉模式的升级版)

懒汉模式有个问题呀,就是synchronized影响程序性能呀,每次newInstance()都有synchronized

那就想个方法,我不给方法synchronized,我先判断有没有singObj,没有,我再加个synchronized,然后再判断一次

public final class DoubleCheckedSingleton
{
    private static DoubleCheckedSingleton singObj = null;

    private DoubleCheckedSingleton(){
    }

    public static DoubleCheckedSingleton getSingleInstance(){
        if(null == singObj ) {                                //第一次检查
              Synchronized(DoubleCheckedSingleton.class){     //加锁
                     if(null == singObj)                      //第二次检查
                           singObj = new DoubleCheckedSingleton();
              }
         }
       return singObj;
    }
}

这里看似没有问题,其实我们都忽略了编译器初始化对象的过程。

singObj = new DoubleCheckedSingleton();

在编译器中,他的步骤是:
1·分配对象域(内存空间);
2·初始化对象;
3·singObj指向对象域(内存空间);

但是在大多数情况下,编译器很有可能对语句进行重排序(这种做法是为了在不改变语句意思的情况下,尽可能减少读取和存储的次数,提高效率

那它的步骤就变成了132;

如果AB线程同时去调用getSingleInstance()

编译器给A线程132的顺序创建对象,singObj指向还未完全初始化的对象域;singObj!=null,而对象初始化需要时间;

B线程同时请求的时候,第一次检查null == singObj返回false,证明有singObj,而singObj指向的对象域中的对象还未完成初始化,这时就发生了不可预知的情况(看到小熊个鬼哦!)

这一切都是编译器的重排序搞的鬼,该如何解决?

很简单,我们在声明singObj的时候添加volatile修饰符。

  • volatile修饰符指明了对象之于线程的可见性(一个线程修改对象,另一个线程是可见的,可知的)
  • 另一个作用就是它禁止了编译器对指令的重排序

在强制编译器按照123的顺序初始化对象后,B线程就会在第一次检查等待singObj的初始化,两个线程都会取到同一个对象。

可是前面说了,编译器的重排序是为了提高效率,加了volatile后,效率就有所下降;

4·懒加载静态内部类(推荐

public class Singleton{

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

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

静态内部类在最初就会被初始化,同时创建了它的内存空间,而其中的对象会在调用的时候被加载(懒加载)

有点类似饿汉模式,只是使用了静态内部类的方法让单例变得线程安全。。。

相关文章

  • 单例模式--我,我,我,还是我

    小熊最近997加班写代码,身体严重透支。没办法,转行去卖房子了。 1·懒汉模式 客户打电话给门店找小熊看房,同事就...

  • C++单例模式的实现分析

    单例模式 什么是单例模式?我就不多做赘述了。移步至百度百科单例模式。 什么时候使用单例? 单例模式是一个经典的设计...

  • 我理解的单例模式

    饿汉模式是在类加载的时候就饿了,非要创建实例,不考虑创建对象的内存开销,而接下来我要介绍的懒汉模式正好可以解决创建...

  • 《设计模式系例(一)》-单例模式

    《设计模式系例(一)》-单例模式 ​ 网上说单例模式是所有模式中最简单的一种模式,巧的是我也这么认为。不过越...

  • 创建型模式:单例模式

    个人博客原文:创建型模式:单例模式 简介 姓名:单例模式 英文名:Singleton Pattern 价值观:我的...

  • 设计模式——单例模式

    1.单例模式介绍 单例模式是应用最广的模式,也是我最先知道的一种设计模式,在深入了解单例模式之前,每当遇到如get...

  • 设计模式之单例模式

    单例模式是应用最广的设计模式之一,面试的时候常常会被要求写个单例模式的Demo,那么我今天就来看看单例模式的相关内...

  • Android设计模式

    目录 单例模式 适配器模式 Builder模式 简单工厂 单例模式 声明:我是全部抄这位大神的文章,作为学习笔记 ...

  • Unity3d游戏开发设计模式之单例设计模式

    后面我将会为大家一一整理出单例模式的写法分类我们先了解熟悉一下什么是单例设计模式: 【单例模式说明】 【前言】最近...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

网友评论

    本文标题:单例模式--我,我,我,还是我

    本文链接:https://www.haomeiwen.com/subject/fftrfctx.html