哪些情况下的单例会被破坏

哪些情况下的单例会被破坏

一、什么是单例

大致意思,确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点

二、可能出现单例对象被破坏的情况

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

 

posted @ 2022-05-03 20:07  编程小白1024  阅读(172)  评论(0编辑  收藏  举报