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


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

    一、什么是单例

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

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

    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; 
      }
    }
  • 相关阅读:
    程序调试的利器GDB
    Building a Android Development Environment
    手机的串号IMEI/ESN标示位置图解摩托罗拉官方教程
    Linux调试信息输出串口设备号的设置
    git忽略文件提交
    Spring @Transactional事务传播范围以及隔离级别
    自定义springbootstarter
    maven 命令将jar包安装到本地仓库
    oracle 常用命令
    Oracle 用户(user)和模式(schema)的区别
  • 原文地址:https://www.cnblogs.com/karrya/p/16219257.html
Copyright © 2020-2023  润新知