单例模式

返回 面试 · 速查全集 Java常见面试题

确保类只有一个实例,并提供全局访问点。详解 设计模式


要求

  • 构造器私有
  • 提供静态获取实例方法
  • 线程安全(多线程环境)
  • 防反射、反序列化破坏(面试加分项)

实现方式对比

方式线程安全延迟加载防反射/序列化推荐
饿汉式❌ 类加载即创建简单场景
懒汉 synchronized性能差
双重检查锁 DCL✅(volatile)常用
静态内部类推荐
枚举❌ 枚举加载最简最安全

双重检查锁(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;
    }
}

为何 volatilenew 非原子,可能重排序导致其他线程看到未初始化完成的对象。


静态内部类(推荐)

public class Singleton {
    private Singleton() {}
 
    private static class Holder {
        static final Singleton INSTANCE = new Singleton();
    }
 
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

JVM 保证内部类首次使用时才加载,天然 lazy + 线程安全。


枚举(Effective Java 推荐)

public enum Singleton {
    INSTANCE;
 
    public void doWork() { /* ... */ }
}

JVM 保证枚举实例唯一;天然防反射和序列化破坏。


破坏单例的方式(面试追问)

手段防御
反射调用私有构造构造器内判 instance != null 抛异常;或改用枚举
反序列化新对象readResolve() 返回 INSTANCE;或枚举
克隆Cloneable 或 clone 返回 INSTANCE

Spring 中的「单例」

Spring 默认 Bean 作用域是 singleton(容器内一个实例),与 GoF 单例模式概念相近但作用域不同(Per Container)。见 Bean作用域


相关笔记