Singleton的另外两个分身IoDH和Enum

226 阅读2分钟

前几天我细数了singleton的4个分身。如今又故技重施,数一数它的另外两个分身。这两个分身相较于其他4个分身,可谓是天壤之别,在jvm中是得天独厚的。各位请看:

IoDH

IoDH(Initialization-on-demand Holder) 只有静态内部类可实例化InnerClassSingleton,且在类加载的时候,静态内部类在使用的时候才会初始化。此实现方式可得天独厚,以其简洁性、高效性和线程安全性脱颖而出。推荐指数🌟🌟🌟🌟🌟!!!

public class InnerClassSingleton {
    private InnerClassSingleton(){
        // 私有构造方法
    }

    private static class InnerClassSingletonHolder{
        private final static InnerClassSingleton INSTANCE = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance(){
        return InnerClassSingletonHolder.INSTANCE;
    }
}
线程安全验证

可通过多线程并发调用 getInstance() 并打印哈希值验证,所有线程应输出相同对象地址。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ConcurrencyTest {
    private static final int THREAD_COUNT = 100;
    private static final CountDownLatch latch = new CountDownLatch(THREAD_COUNT);
    private static volatile InnerClassSingleton instance;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT);
        
        for (int i = 0; i < THREAD_COUNT; i++) {
            executor.execute(() -> {
                try {
                    latch.await();
                    InnerClassSingleton current = InnerClassSingleton.getInstance();
                    // 检查是否所有线程获取的是同一实例
                    if (instance == null) {
                        instance = current;
                    } else if (instance != current) {
                        System.out.println("发现不同实例!");
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            latch.countDown();
        }
        
        executor.shutdown();
        Thread.sleep(2000);
        System.out.println("所有线程验证通过");
    }
}
优势
  • 延迟加载:实例在首次调用 getInstance() 时创建。
  • 线程安全:由 JVM 保证类加载过程的线程安全性,无需 synchronized
  • 高性能:避免双重检查锁(DCL)的同步开销。
JDK中的类似设计

虽然JDK未直接使用IoDH,但体现了其思想:Collections.emptyList() : 通过静态内部类 EmptyList 实现空集合的延迟加载和线程安全。

    public static final List EMPTY_LIST = new EmptyList<>();
    private static class EmptyList<E> extends AbstractList<E> implements RandomAccess {}

利用枚举

利用枚举类的独特优势,无法使用反射等机制进行实例化,天然就是单利的魅力。连私有的构造,此货都是不需要的了,堪称神级设计。推荐指数🌟🌟🌟🌟🌟!!!

public enum EnumSingleton {
    INSTANCE;
    public void method1(){
        // 业务逻辑
    }

    public void method2(){
        // 业务逻辑
    }

    public void method3(){
        // 业务逻辑
    }

    // 调用方式: EnumSingleton.INSTANCE.method1();
}

IoDH 和枚举单例哪个更好?

  • IoDH:适合需要延迟加载的场景,代码更灵活。
  • 枚举单例:天然防反射和序列化破坏,由 JVM 保证唯一性。

总结

在像孔乙己一样,研究了回字的四种写法 。哦,是历数了单利的6个分身之后。也许你会有此疑问,难不成在开发的时候,我要时刻默数分身,深深陷入选择困难症中?不不不,这不是我推荐的。够用就好!Hungry很亲民的,好写又好用。“吃了吗?”是我们日常问候语。“饿了吗?”也是我们的日常问候语。实在没必要成为“孔乙己”,共勉!