Head First设计模式学习笔记五

单件模式

Posted by Emo on July 14, 2022

设计模式

练习实例仓库

EMO’s Blog

单件模式

单件模式 确保一个类只有一个实例,并提供一个全局访问点。

为什么要用单件模式

单件模式的作用就是实例化一个全局对象,并确保其是唯一的。那么为什么不直接使用全局变量呢?

全局变量必须在程序运行初期就统一创建,这样如果一个全局变量很少甚至基本没有用到,它依旧会持续的占用大量内存资源。但是通过单件模式,我们既为对象提供了全局访问点,又可以只在需要的时候才使用对象。

单件模式的结构

按照传统的类结构,我们可以发现一个类的实例化次数是不受限制的。当我们需要构建一个唯一的只能被实例化一次的对象的时候,我们需要考虑到如何去限制他的实例化次数。

可以试想,如果我们将类的构造函数设置为private,那么会怎样?这个时候类的外部将无法实例化该类,唯一的手段只有从类的内部来进行实例化。这个时候我们就可以在类的内部设置限制来控制类的实例化次数了。而后通过static的方法,为外部提供一个受控制受约束的实例化方法。

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

按照上面机构构建的类很有趣很独特的一个点就是它没有公开的构造器,这样独特的结构让它具备了三个优点:

  1. 确保该类只会有一个对象
  2. 能够延迟实例化(Lazy instantiaze),在需要的时候再实例化该对象
  3. 具备全局访问点,只需要调用类方法getInstance就可以获取到全局唯一的实例

单间模式的线程问题

我们可以看到在getInstance方法当中的代码是没有线程保护的,那么在多线程的情况下,初始化唯一实例的时候就可能会产生冲突。要解决这个问题有三种方案:

  1. 使用synchronized同步,优点是简单,缺点是会带来性能下降,因为每一次调用getinstance都需要同步。
public static synchronized getInstance(){}
  1. 放弃延迟实例化,在类中直接实例化一个static的对象。
public class Singleton {
    private static uniqueSingleton = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return uniqueSingleton;
    }
}
  1. 用“双重检查加锁(double-checked locking)”,在getInstance中减少使用同步。这里需要使用到volatile

    volatile可以看作是一种轻量化的synchronized,他只保证了共享变量的可见性。不具备可见性的变量被写入主存中的时间是不可定的,也就是说,如果我们为uniqueSingleton赋值后,可能在其他线程访问它的时候,主存中并没有更新,因此可能会产生冲突。而使用volatile后,则在初始化变量的时候,会使其具备可见性,能确保其被写入主存当中后才会被其他线程访问到。但是非原子操作依旧可能带来线程安全问题。比如 volatile int a = 0 之后进行 a++,那么 a++ 这个操作是不具备原子性的,其他线程依旧可能读到旧值。

public class Singleton {
    private volatile static Singleton uniqueSingleton;
    private Singleton() {}
    public static Singleton getInstance() {
        // 只有当检测到uniqueSingleton为null的时候才发起同步的流程,检查并初始化单例。
        if (uniqueSingleton == null) {
            synchronized (Singleton.class) {
                if (uniqueSingleton == null) {
                    uniqueSingleton = new Singleton();
                }
            }
        }
        return uniqueSingleton;
    }
}

{ % if page.mermaid % } { % endif % }