单例模式

作者: rdgbrain | 来源:发表于2020-07-09 20:28 被阅读0次

单例模式

  • 当系统中对于某个类,只需要有一个实例化对象时,可以使用单例模式。

单例模式实现方式

单例模式的实现主要有两个特征

  1. 私有化构造方法,让外部不能直接通过构造方法创建对象实例;
  2. 提供一个静态的公共方法( 例如下方的getInstance() ),外部通过这个方法获取对象实例。

在系统启动时即创建--俗称饿汉式

public class Singleton {

    private Singleton() {

    }
    private static Singleton singleton = new Singleton();

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

第一次调用时才创建--俗称懒汉式

public class Singleton {

    private Singleton() {

    }
    private static Singleton singleton;

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

懒汉式单例模式存在的问题

在并发编程的场景下,如果多个线程同时调用getInstance()方法,可能会存在创建多个对象实例的情况,为了避免这种错误的问题产生,可以使用锁来解决问题。

1. 给方法加锁
public class Singleton {

    private Singleton() {

    }
    private static Singleton singleton;

    public synchronized static Singleton getInstance() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
  • 这种方式开销过大,每个线程进入方法前都要获得锁;如果实例已经被创建,访问时直接返回即可,不存在线程安全问题
2. 锁代码块
public class Singleton {

    private Singleton() {

    }
    private static Singleton singleton;

    public  static Singleton getInstance() {
        if (singleton == null) {            
            synchronized (Singleton.class) {
                
                singleton = new Singleton();
            }
            
        }
        return singleton;
    }
}
  • 这种方式的好处是:当实例已经创建时,线程访问时就不需要再获取锁,可以直接返回。实例不存在时,保证只有单个线程在创建实例

但是这种加锁方式依然存在问题,假如有两个线程A,B调用getInstance()方法,在线程A还没有实例化完成时,线程B也可以通过if判断进入内部,等待锁释放并创建对象,此时对象依然创建了多个。


temp.png

为了解决这种问题,于是提出了以下方式。

3. DCL(Double Check Lock)双重检查锁
public class Singleton {

    private Singleton() {

    }
    private static Singleton singleton;

    public  static Singleton getInstance() {
        if (singleton == null) {            
            synchronized (Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
                
            }
            
        }
        return singleton;
    }
}
  • 获取锁后,在进行一次为空判断,这样就可以解决上面提出的问题

DCL的方式看起来似乎完美无缺,但遗憾的是,其中依然存在问题。在说明问题前,先了解下什么是“指令重排序”

  • 指令重排序
    JVM在执行代码时,对没有数据依赖的操作步骤,可能会以指令重排序的方式执行代码,提高执行效率。如下图:


    cpx.png

所以DCL模式也会出现以下问题


dcl.png

因此,线程可能会获取到一个未初始化完成的对象


dcl1.png
4. 最终写法
  • 使用volatile修饰singleton

volatile可以禁止指令重排序,保证内存可见性。具体实现原理参考《java并发编程的艺术》一书。限于篇幅,不再说明。

public class Singleton {

    private Singleton() {

    }
    private volatile static Singleton singleton;

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

相关文章

  • 【设计模式】单例模式

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

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 单例

    iOS单例模式iOS之单例模式初探iOS单例详解

  • 单例模式

    单例模式1 单例模式2

  • java的单例模式

    饿汉单例模式 懒汉单例模式

网友评论

    本文标题:单例模式

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