跳转至

写出三种单例模式实现

一、饿汉式

类加载时就初始化,线程安全:

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}
  • ✅ 简单、线程安全。
  • ❌ 类加载就创建,不懒加载。

二、双重检查锁(DCL)

public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次检查
            synchronized (Singleton.class) {
                if (instance == null) {            // 第二次检查
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
  • ✅ 懒加载、线程安全、高性能。
  • ❌ 必须用 volatile,防止指令重排。

为什么要 volatile

new Singleton() 分三步: 1. 分配内存。 2. 初始化对象。 3. 引用指向内存。

JIT 可能重排成 1-3-2,另一个线程看到 instance 非 null 但对象还没初始化。

三、静态内部类

public class Singleton {
    private Singleton() {}
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}
  • ✅ 懒加载(Holder 类第一次用才加载)。
  • ✅ 线程安全(JVM 保证)。
  • ✅ 不需要 volatile。

推荐写法

四、枚举(最推荐)

public enum Singleton {
    INSTANCE;
    public void method() {}
}
  • ✅ 线程安全。
  • ✅ 防反射破坏。
  • ✅ 防反序列化重建。

Effective Java 推荐。

五、对比

写法 懒加载 线程安全 防反射
饿汉式
DCL
静态内部类
枚举

高频追问

  • 反射怎么破坏单例?getDeclaredConstructor().setAccessible(true).newInstance()
  • 枚举为什么防反射?JDK 规定反射创建枚举会抛异常。