单例模式引出的知识点

Posted by 孙继峰 on May 7, 2020

WHY

  • 对于频繁使用的对象,可以省略创建对象所花费的时间, 这对于那些重量级对象而言,是非常可观的一笔系统开销;

  • 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低, 这将减轻 GC 压力,缩短 GC 停顿时间。

HOW

Eager Singleton

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
        return instance;  
    }  
}
上面的饿汉式单例模式的知识点

通过定义静态成员变量以保证单例对象可以在类初始化的过程中被实例化。
这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。 所以这个方法默认在整个装载过程中都是线程安全的。所以在类加载过程中对象的创建也是线程安全的。 其实现如下

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException{
        synchronized (getClassLoadingLock(name)) {
            // do something
        }
}


Eager Singleton

public enum Singleton {
    INSTANCE;
    public void method() {}
}
上面枚举的单例模式的知识点

枚举其实底层是依赖Enum类实现的,这个类的成员变量都是static类型的, 并且在静态代码块中实例化的,和饿汉有点像, 所以他天然是线程安全的。 下面是枚举类反编译的结果

public final class Singleton extends Enum<EnumSingleton> {
  public static final EnumSingleton INSTANCE;
  public static EnumSingleton[] values();
  public static EnumSingleton valueOf(String value);
  public void method();
  static {};
}

由于反编译工具不同编译出的代码会不同, 以上为 javap 反编译的结果


DCL Lazy Singleton

public class Singleton {
    private volatile static Singleton singleton = null;
    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
DCL 单例涉及的知识点
  • volatile: 保证内存可见性, 对其修饰变量的修改会立即可见
  • 指令重排序: 创建对象可以分为这几部: 1.分配内存 2.初始化对象 3.引用指向内存地址, 但是经过重排序可能会变成 132, 通过使用 volatile 来禁止重排序

CAS Lazy Singleton

public class Singleton {
    private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();
    private Singleton() {}

    public static Singleton getInstance() {
        for (;;) {
            Singleton singleton = INSTANCE.get();
            if (null != singleton) {
                return singleton;
            }

            singleton = new Singleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                return singleton;
            }
        }
    }
}
CAS 单例中的知识点
  • AtomicReference: JUC 中的原子引用, 对上面提到的 volatile 进行包装
  • CAS: 比较并交换, 相对于 Lock 与 Synchronized 更为轻量级的乐观锁