# 一问一答学设计模式 -- 单例模式

163 阅读2分钟

单例模式:顾名思义,就是只有一个实例的模式

:为什么需要单例?

:有一些对象只需要一个实例,比如线程池、缓存、注册表......如果有多个实例,可能会导致数据不一致,资源占用高等问题。

:全局变量不就可以做到定义变量,全局使用吗?

:全局变量存在缺点。

  1. 使用前初始化时就会存在这个实例,但是在某些执行过程又不需要,造成资源的浪费。
  2. 全局变量只是提供一个变量的全局访问,但是不能确保实例只有一个。

:怎么实现单例模式

:单线程的情况下可以这么做,主要三点:静态变量,私有构造器和静态获取实例的方法

public class Singleton {
    // 1. 记录实例的静态变量
    private static Singleton uniqueInstance;
    
    // 2. 私有化构造器
    private Singleton() {}
    
    // 3. 静态方法实例化对象,并返回实例
    public static Singleton getInstance() {
        if(uniqueInstance == null) {
            // 判断实例为空再允许创建
            uniqueInstance = new Singleton();
        }
        // 返回这个全局引用
        return uniqueInstance;
    }
}

:多线程下要怎么做?

: 多线程并发时,由于线程挂起等原因,可能导致出现两个实例。解决方式有三种:

  1. 在 getInstance 前加 synchronized 关键字,同步这个方法。(性能要求不高的情况下)
  2. “急切”创建实例。在声明全局变量时就初始化好该实例。(资源不敏感的情况下)
  3. 双重检查加锁,在方法1的基础上,避免第一次初始化资源后,之后每次获取资源还要锁消耗的问题。
public class Singleton {
    // 1. 记录实例的静态变量
    private volatile static Singleton uniqueInstance;
    
    // 2. 私有化构造器
    private Singleton() {}
    
    // 3. 静态方法实例化对象,并返回实例
    public static Singleton getInstance() {
        if(uniqueInstance == null) {
            // 只有第一次创建对象时才需要锁
            synchronized (Singleton.class) {
                // 进入后二次检查,确保实例未被创建
                if(uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

总结

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

  1. 要获取实例,通过单例类是唯一的方式。(不考虑反射等非正常手段)
  2. 利用延迟实例化的方式,对资源敏感的对象是很重要的。

参考链接

更多的单例模式实现方式可参考 github.com/biyunjue/le…