单例不只是把构造方法设为 private。还要回答:对象何时创建,并发第一次访问时会不会重复创建,以及其他线程能否安全看到初始化结果。

提前创建与延迟创建

静态字段直接初始化,是常见的饿汉式写法:

public final class EagerSingleton {
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    private EagerSingleton() {}
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

它在类初始化时创建,而不是简单等同于“JVM 一启动就创建”。懒汉式延迟到首次访问,但普通的 if (instance == null) 不加同步,会让多个线程同时通过检查。

同步方法最容易解释

public final class SynchronizedSingleton {
    private static SynchronizedSingleton instance;
    private SynchronizedSingleton() {}
    public static synchronized SynchronizedSingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedSingleton();
        }
        return instance;
    }
}

返回应在 if 之后,确保已经初始化时仍返回实例。同步覆盖检查和创建,也建立可见性关系。是否值得优化访问开销,应按实际场景度量,不能凭模式名称断言它“性能一定很差”。

双重检查为什么需要 volatile

public final class DclSingleton {
    private static volatile DclSingleton instance;
    private DclSingleton() {}
    public static DclSingleton getInstance() {
        if (instance == null) {
            synchronized (DclSingleton.class) {
                if (instance == null) {
                    instance = new DclSingleton();
                }
            }
        }
        return instance;
    }
}

外层检查减少初始化后的加锁;内层检查防止等待锁的线程再次创建。volatile 参与安全发布,不能因为创建发生在 synchronized 内,就省掉外部读取所需的可见性保证。这里按 Java 5 及之后的内存模型理解。JLS 17.4.5

静态 Holder 利用类初始化机制

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

访问 Holder.INSTANCE 时触发持有类的初始化,由类初始化机制协调并发。它把延迟创建与安全初始化交给 JVM,而不是手写双重检查。JLS 类初始化过程

唯一实例不等于所有操作都线程安全

这些常规写法的范围与类加载器有关,不是整个进程不可能出现第二个同类实例的安全边界。反射、序列化及框架作用域还有各自约束。

更重要的是:实例只创建一次,不代表里面的可变集合、计数器或复合业务操作自动安全。初始化、发布与后续状态并发必须分别设计。