小易说IT 小易说IT

Java 单例模式 5 种常见实现

单例核心:保证一个类在整个 JVM 中只有一个实例对象,提供全局访问点

1. 饿汉式(静态常量,线程安全,简单常用)

类加载直接实例化,没有懒加载,类初始化就创建对象。

/**
 * 饿汉式单例
 * 优点:实现简单,天然线程安全
 * 缺点:类加载就创建实例,没有懒加载,如果对象永远不用,造成内存浪费
 */
public class SingletonHungry {

    // 类加载直接初始化实例
    private static final SingletonHungry INSTANCE = new SingletonHungry();

    // 私有构造器,禁止外部new
    private SingletonHungry() {}

    // 对外获取实例方法
    public static SingletonHungry getInstance() {
        return INSTANCE;
    }
}

2. 懒汉式(非线程安全,禁止生产使用)

需要的时候才创建实例,懒加载,多线程会创建多个对象。

/**
 * 懒汉式,线程不安全,多线程环境会失效,仅做演示,不要上生产
 */
public class SingletonLazy {
    private static SingletonLazy instance;

    private SingletonLazy() {}

    public static SingletonLazy getInstance() {
        if (instance == null) {
            instance = new SingletonLazy();
        }
        return instance;
    }
}

3. 双重检查锁 DCL(Double‑Checked Locking,生产最常用)

线程安全、懒加载、性能好,业务开发首选,必须加 volatile 禁止指令重排。

/**
 * DCL双重检查锁单例模式【推荐业务使用】
 * volatile:防止new对象时指令重排导致半初始化对象被返回
 */
public class SingletonDCL {

    // volatile非常关键
    private static volatile SingletonDCL instance;

    private SingletonDCL() {
        // 防止反射暴力破解单例(可选增加防御)
        if(instance != null){
            throw new RuntimeException("禁止反射创建实例");
        }
    }

    public static SingletonDCL getInstance() {
        // 第一次判断,避免每次竞争锁
        if (instance == null) {
            synchronized (SingletonDCL.class) {
                // 第二次判断,防止多线程等待锁后重复创建
                if (instance == null) {
                    instance = new SingletonDCL();
                }
            }
        }
        return instance;
    }
}

new SingletonDCL() 分三步:分配内存 → 初始化对象 → 赋值引用;不加 volatile,JVM 可能重排顺序,返回未初始化完成的对象。

4. 静态内部类单例(优雅,线程安全,懒加载)

利用类加载机制,外部类加载不会加载内部类,调用 getInstance 才加载,推荐

/**
 * 静态内部类单例
 * 优点:懒加载、线程安全、无锁,代码简洁
 * 缺点:反射依然可以破坏单例
 */
public class SingletonInner {

    private SingletonInner(){}

    // 静态内部类,只有调用才会加载
    private static class SingletonHolder{
        private static final SingletonInner INSTANCE = new SingletonInner();
    }

    public static SingletonInner getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

5. 枚举单例(Effective Java 推荐,防止反射、序列化破坏)

最安全,杜绝反射、序列化漏洞,缺点不是懒加载

/**
 * 枚举实现单例,《Effective Java》推荐
 * 天然防止反射、序列化破坏单例
 */
public enum SingletonEnum {
    INSTANCE;

    // 业务方法写在这里
    public void doSomething(){
        System.out.println("枚举单例执行业务逻辑");
    }
}

使用示例

public class TestMain {
    public static void main(String[] args) {
        SingletonDCL s1 = SingletonDCL.getInstance();
        SingletonDCL s2 = SingletonDCL.getInstance();
        System.out.println(s1 == s2); // true

        SingletonEnum.INSTANCE.doSomething();
    }
}

各方案对比总结

实现方式

懒加载

线程安全

防反射 / 序列化

适用场景

饿汉式

对象占用小,一定会被使用

普通懒汉

禁止生产使用

DCL 双重检查

绝大多数业务场景

静态内部类

追求简洁无锁

枚举

需要绝对安全,不怕提前加载

注意:DCL、静态内部类都可以被反射暴力调用私有构造器破坏单例;枚举是 Java 底层语法层面规避该问题。


本文原创作者:易君召,详见:https://www.yijunzhao.cc/about,转载请注明出处。

原文链接 https://www.yijunzhao.cc/archives/java-singleton-pattern-5-common-implementations

欢迎访问 https://www.yijunzhao.cc/

https://www.yijunzhao.cc/