简洁全面的单例模式整理

单例实现

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

1.静态内部类

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

    private Singleton() {
    }

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

2.饿汉模式

通过定义静态的成员变量,以保证单例对象可以在类初始化的过程中被实例化。
这其实是利用了ClassLoader的线程安全机制。ClassLoader的loadClass方法在加载类的时候使用了synchronized关键字。
所以, 除非被重写,这个方法默认在整个装载过程中都是线程安全的。所以在类加载过程中对象的创建也是线程安全的。

public class Singleton {

    private static final Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

3.懒汉模式

双重校验锁方式实现单例。

为什么用volatile修饰?

因为编译器有可能进行指令重排优化,使得singleton对象再未完成初始化之前就对其进行了赋值,这样其他人拿到的对象就可能是个残缺的对象了。使用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;
    }

}

4.枚举实现

枚举底层依赖Enum类实现的,这个类的成员变量都是static类型的,并且在静态代码块中实例化,保证线程安全。

枚举可以解决反序列化破坏单例的问题。

在枚举序列化的时候,Java仅仅是将枚举对象的name属性输出到结果中,反序列化的时候则是通过java.lang.Enum的valueOf方法来根据名字查找枚举对象。同时,编译器是不允许任何对这种序列化机制的定制的,因此禁用了writeObject、readObject、readObjectNoData、writeReplace和readResolve等方法。
普通的Java类的反序列化过程中,会通过反射调用类的默认构造函数来初始化对象。所以,即使单例中构造函数是私有的,也会被反射给破坏掉。由于反序列化后的对象是重新new出来的,所以这就破坏了单例。
但是,枚举的反序列化并不是通过反射实现的。所以,也就不会发生由于反序列化导致的单例破坏问题。


public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

5.CAS实现

 private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<Singleton>();

    private Singleton() {}

    public static Singleton getInstance() {
        for (;;) {
            Singleton singleton = INSTANCE.get();
            if (null != singleton) {
                return singleton;
            }

            singleton = new Singleton();
            if (INSTANCE.compareAndSet(null, singleton)) {
                return singleton;
            }
        }
    }

单例破坏

反射破坏

private void distoryByReflect() {
    Singleton singleton = Singleton.getSingleton();
    try {
        Class<Singleton> singleClass = (Class<Singleton>)Class.forName("com.esparks.pandora.learning.designmode.Singleton");

        Constructor<Singleton> constructor = singleClass.getDeclaredConstructor(null);

        constructor.setAccessible(true);

        Singleton singletonByReflect = constructor.newInstance();

        System.out.println("singleton : " + singleton);
        System.out.println("singletonByReflect : " + singletonByReflect);
        System.out.println("singleton == singletonByReflect : " + (singleton == singletonByReflect));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

解决方式:在Singleton的构造函数中增加如下代码

private Singleton() {
    if (singleton != null) {
        throw new RuntimeException("Singleton constructor is called... ");
    }
}

序列化破坏

通过先序列化再反序列化的方式,可获取到一个新的单例对象,这就破坏了单例。

private void distoryBySerialize() {
    Singleton singleton = Singleton.getSingleton();

    //Write Obj to file
    ObjectOutputStream oos = null;
    try {
        oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
        oos.writeObject(singleton);
        //Read Obj from file
        File file = new File("tempFile");

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Singleton singletonBySerialize = (Singleton)ois.readObject();
        //判断是否是同一个对象

        System.out.println("singleton : " + singleton);
        System.out.println("singletonBySerialize : " + singletonBySerialize);
        System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize));

    } catch (Exception e) {
        e.printStackTrace();
    }
}

解决方式:在Singleton中增加readResolve方法,并在该方法中指定要返回的对象的生成策略几可以了。即序列化在Singleton类中增加以下代码即可:

private Object readResolve() {
    return getSingleton();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章