返回

单例模式的非传统实现:绕开 synchronized 和 lock

见解分享

在并发编程中,单例模式至关重要,它确保了一个类只有一个实例。传统上,我们使用 synchronized 或 lock 来实现线程安全性,但有更优雅的方法可以实现这一点,让我们深入探讨一下。

静态内部类

静态内部类是一种嵌套类,只有在外部类被加载后才会加载。Java 类加载器在加载类时遵循单例模式,因此静态内部类的实例在第一次被引用时才会被初始化。

public class Singleton {

    private static class SingletonHelper {
        private static final Singleton INSTANCE = new Singleton();
    }

    private Singleton() {}

    public static Singleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

枚举

枚举也是一种特殊类型的类,它本质上是单例的。枚举中的每个实例都是该类的一个单例,并且在类加载时就初始化。

public enum Singleton {
    INSTANCE;

    public void doSomething() {
        // ...
    }
}

饿汉式(延迟初始化)

饿汉式单例在类加载时立即创建实例,并使用 volatile 确保线程可见性。

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;
    }
}

为什么借助了 ClassLoader?

Java 类加载器在加载类时遵循单例模式。当一个类被加载时,类加载器会检查该类是否已经被加载。如果已经被加载,它将返回已经加载的类实例,否则会创建一个新的实例。

静态内部类、枚举和饿汉式单例都利用了这一特性。静态内部类仅在外部类被加载时加载,枚举在类加载时被初始化,而饿汉式单例在类加载时立即创建实例。

结论

除了传统的 synchronized 和 lock,还有许多其他方法可以实现单例模式。静态内部类、枚举和饿汉式(延迟初始化)都是非传统但有效的替代方案。这些方法利用了 Java 类加载器的单例特性,提供了一种线程安全且优雅的单例实现。