单例模式4-注册式单例(枚举,容器)

注册式单例(两种:枚举、容器)

枚举单例模式

网上说的都有问题,不如百度经验讲的清楚

public enum SingletonEnum {
    /**
     * 单例
     */
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static SingletonEnum getInstance() {
        return INSTANCE;
    }
}

反编译:

    public static final SingletonEnum INSTANCE;
    private Object data;
    private static final SingletonEnum $VALUES[];

    static 
    {
        INSTANCE = new SingletonEnum("INSTANCE", 0);
        $VALUES = (new SingletonEnum[] {
            INSTANCE
        });
    }

反编译发现INSTANCE被静态代码块初始化了,是一种饿汉式单例。
测试(这里不放代码和原理):

  1. 开2000条线程去getInstance(),没有线程安全问题,保证单例。
  2. 序列化反序列化之后看是否为同一对象,结果是的。
  3. 对enum进行反射,失败。
  4. 不能保证直接对data进行反射,对data进行反射还是可以实例化的。
    这里主要还是利用了enum的一些特性,从反编译就可以看出,enum是天生单例且线程安全的,这种线程安全是jvm层面的,因为jvm保证类加载是线程安全的。其次,枚举序列化的时候用的是类名和class来找到唯一枚举对象,所以序列化也能保证单例。最后反射enum的时候也有保证,不论是你反射用了无参的,还是enum(String.class,int.class),底层都会被阻拦,因为如果是enum类型就会抛出异常,这是jdk层面的保护。但是并不能保证非枚举类型进行反射咯,比如上面的data还是可以被反射出来的。

容器式单例

public class ContainerSingleton {

    /**
     * 构造器私有
     */
    private ContainerSingleton() {
    }

    private static final Map ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }

}

spring也是这么干的,AbstractAutowireCapableBeanFactory抽象类;

你可能感兴趣的:(单例模式4-注册式单例(枚举,容器))