哪些情况下的单例会被破坏
一、什么是单例
大致意思,确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点
二、可能出现单例对象被破坏的情况
1、多线程破坏单例
2、指令重拍破坏单例
3、克隆破坏单例
4、反射破坏单例
1、多线程破坏单例
只会出现在懒汉模式中,恶汉模式在线程启动前就已经被初始化了。
如何解决懒汉模式的单例在多线程情况下被破坏的情况:采用DCL检索模式、使用静态内部类的写法
双检锁/双重校验锁(DCL,即 double-checked locking)
public class Singleton {
private static Singleton singleton;
/**
* 对外提供一个可访问的方法
*/ public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
静态内部类:
public class Singleton {
private Singleton (){} // 私有化构造器
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
2、指令重排破坏单例
问题:JVM指令重排可能导致懒汉模式单例被破坏
instance = new Singleton();
执行指令:
memory = allocate(); // 1、分配内存
ctorInstance(memory); // 2、初始化对象
instance = memory; // 3、赋值引用
指令重排:
memory = allocate(); // 1、分配内存
instance = memory; // 2、赋值引用
ctorInstance(memory); // 3、初始化对象
线程t1初始化完成这段内存之后,线程t2虽然不能去同步代码块,判断instance不为空,此时t2获得instance对象如果直接使用就有可能发生错误
解决方案:在采用双检锁/双重校验锁(DCL,即 double-checked locking),使用volatile关键字保证单例不存在指令重排
public class Singleton { private volatile static Singleton singleton; //使用volatile关键字进行修饰,防止指令重排/** * 对外提供一个可访问的方法 */ public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
3、克隆破坏单例
在JAVA中,我们所有的类都是继承至Object,所有的类都实现了clone()方法,如果是深clone(),每次都会创建新的实例,
如何保证创建的对象是单例呢?
我们可以重写clone()方法,将单例自身的引用作为返回值
class Single { //创建 Single 的一个对象 private static Single instance = new Single(); //让构造函数为 private,这样该类就不会被实例化 private Single(){} //获取唯一可用的对象 public static Single getInstance(){ return instance; } @Override protected Object clone() throws CloneNotSupportedException { return this.instance; } }
4、反射破坏单例
java中的反射是可以拿到私有化构造方法的,可以任意调用私有构造方法创建对象。
如何处理:
1、所有私有化构造方法中判断单例对象是否被创建,如果没有被创建就抛出异常
2、使用枚举类型实现单例模式,JDK源码里面规定了,不能通过反射来访问枚举
//枚举单例模式 public enum EnumSingleton { INSTANCE; public EnumSingleton getInstance(){ return INSTANCE; } }