zoukankan      html  css  js  c++  java
  • 单例模式,反射破环?

    饿汉式

    // 饿汉式单例
    public class Hungry {
        
        //构造器私有
        private Hungry(){
    
        }
        // 一上来就把这个类加载了
        private final static  Hungry HUNGRY = new Hungry();
    
        public static Hungry getInstance(){
            return HUNGRY;
        }
    }
    
    // 饿汉式单例
    public class Hungry {
        // 这4组数据非常耗内存资源,饿汉式一上来就把所有的内存里面的东西全部加载进来了,就存在这个空间
        // 但这个空间现在是没有使用的,可能会造成浪费空间
        private byte[] data1 = new byte[1024*1024];
        private byte[] data2 = new byte[1024*1024];
        private byte[] data3 = new byte[1024*1024];
        private byte[] data4 = new byte[1024*1024];
        
        //构造器私有
        private Hungry(){
    
        }
        // 一上来就把这个类加载了
        private final static  Hungry HUNGRY = new Hungry();
    
        public static Hungry getInstance(){
            return HUNGRY;
        }
    
    }
    

    饿汉式单例可能会造成浪费空间,所以想要用的时候再去创建这个对象,平时就先放在这个地方,于是就出现了懒汉式!

    懒汉式

    // 懒汉式单例
    public class LazyMan {
    	
       // 构造器私有
        private LazyMan(){
           
        }
    
        private  static LazyMan lazyMan;
    
       
        public static LazyMan getInstance(){
           
            if (lazyMan==null){
                lazyMan = new LazyMan(); 
            }
            return lazyMan;
        }
    }
    

    它是有问题的,单线程下确实单例ok,多线程并发就会出现问题!

    测试

    // 懒汉式单例
    public class LazyMan {
    	
       // 构造器私有
        private LazyMan(){
           System.out.println(Thread.currentThread().getName()+":: ok");
        }
    
        private  static LazyMan lazyMan;
    
       
        public static LazyMan getInstance(){
           
            if (lazyMan==null){
                lazyMan = new LazyMan(); 
            }
            return lazyMan;
        }
        
        public static void main(String[] args) {
    
            for (int i = 0; i < 10; i++) {
                new Thread(() -> {
                    LazyMan.getInstance();
                }).start();
            }
        }
    }
    

    发现单例有问题,每次结果可能都不一样!

    解决

    // 懒汉式单例
    public class LazyMan {
    
    
        private LazyMan(){
            System.out.println(Thread.currentThread().getName()+":: ok");
        }
    
        private  static LazyMan lazyMan;
    
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance(){
            if (lazyMan==null){
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan(); 
                    }
                }
            }
            return lazyMan;
        }
    
         public static void main(String[] args) {
    
             for (int i = 0; i < 10 ; i++) {
                 new Thread(()->{
                    LazyMan.getInstance();
                 }).start();
             }
         }
    }
    

    但在极端情况下还是可能出现问题

    经历三个步骤:

    1、 分配内存空间

    2、 执行构造方法,初始化对象

    3、 把这个对象指向这个空间

    有可能会发生指令重排的操作!

    比如,期望它执行 123 ,但是它真实可能执行132,比如第一个A线程过来执行了132,先分配空间再吧这个空间占用了,占用之后再去执行构造方法,如果现在突然来了个B线程,由于A已经指向这个空间了,它会以为这个 lazyMan 不等于 null ,直接return ,此时lazyMan还没有完成构造,所以必须避免这个问题!

    必须加上volatile

    // 懒汉式单例
    public class LazyMan {
    
    
        private LazyMan(){
            System.out.println(Thread.currentThread().getName()+":: ok");
        }
    	// 避免指令重排
        private volatile static LazyMan lazyMan;
    
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance(){
            if (lazyMan==null){
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan(); 
                    }
                }
            }
            return lazyMan;
        }
    
         public static void main(String[] args) {
    
             for (int i = 0; i < 10 ; i++) {
                 new Thread(()->{
                    LazyMan.getInstance();
                 }).start();
             }
         }
    }
    

    静态内部类

    // 静态内部类
    public class Holder {
        private Holder(){
    
        }
        public static Holder getInstance(){
            return InnerClass.HOLDER;
        }
        public static  class InnerClass{
            private static final Holder HOLDER = new Holder();
        }
    }
    

    也是单例模式的一种,不安全!

    单例不安全 反射

    // 懒汉式单例
    public class LazyMan {
    
    
        private LazyMan(){
            System.out.println(Thread.currentThread().getName()+":: ok");
        }
    
        private volatile static LazyMan lazyMan;
    
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance(){
            if (lazyMan==null){
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan(); //不是一个原子性操作
                    }
                }
            }
            return lazyMan;
        }
    
    
        //反射
         public static void main(String[] args) throws Exception {
             LazyMan instance1 = LazyMan.getInstance();
             Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
             declaredConstructor.setAccessible(true); // 无视了私有的构造器
             // 通过反射创建对象
             LazyMan instance2 = declaredConstructor.newInstance();
    
             System.out.println(instance1);
             System.out.println(instance2);
         }
    }
    

    结论:反射可以破坏这种单例

    解决

    // 懒汉式单例
    public class LazyMan {
    
        private LazyMan(){
            synchronized (LazyMan.class){
                if (lazyMan!=null){
                    throw new RuntimeException("不要试图使用反射破环 异常");
                }
            }
            System.out.println(Thread.currentThread().getName()+":: ok");
        }
    
        private volatile static LazyMan lazyMan;
    
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance(){
            if (lazyMan==null){
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan(); //不是一个原子性操作
                    }
                }
            }
            return lazyMan;
        }
    
        //反射
         public static void main(String[] args) throws Exception {
             LazyMan instance1 = LazyMan.getInstance();
             Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
             declaredConstructor.setAccessible(true); // 无视了私有的构造器
             // 通过反射创建对象
             LazyMan instance2 = declaredConstructor.newInstance();
    
             System.out.println(instance1);
             System.out.println(instance2);
         }
    }
    

    但是如果都用反射创建对象的情况下,还是会破环单例!

    测试

    解决

    // 懒汉式单例
    public class LazyMan {
          // 标志位
        private static boolean abc = false;
    
        private LazyMan(){
            synchronized (LazyMan.class){
                if (abc==false){
                    abc=true;
                }else {
                    throw new RuntimeException("不要试图使用反射破环 异常");
                }
              
            }
            System.out.println(Thread.currentThread().getName()+":: ok");
        }
        private volatile static LazyMan lazyMan;
    
        // 双重检测锁模式的 懒汉式单例 DCL懒汉式
        public static LazyMan getInstance(){
            if (lazyMan==null){
                synchronized (LazyMan.class){
                    if (lazyMan==null){
                        lazyMan = new LazyMan(); //不是一个原子性操作
                    }
                }
            }
            return lazyMan;
        }
    
        //反射
        public static void main(String[] args) throws Exception {
            //LazyMan instance1 = LazyMan.getInstance();
            Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true); // 无视了私有的构造器
            // 通过反射创建对象
            LazyMan instance2 = declaredConstructor.newInstance();
            LazyMan instance1 = declaredConstructor.newInstance();
    
            System.out.println(instance1);
            System.out.println(instance2);
        }
    }
    

    但是如果被人知道 abc这个变量,也可以破环!

    单例又被破环了!

    看一下源码

    它说不能使用反射破环枚举,枚举是jdk1.5出现的,自带单例模式!

    测试,写一个枚举类

    // enum 本身就是一个class类
    public enum EnumSingle {
    
        INSTANCE;
        public EnumSingle getInstance(){
            return INSTANCE;
        }
    }
    

    查看它的源码

    试图破环!

    // enum 本身就是一个class类
    public enum EnumSingle {
    
        INSTANCE;
        public EnumSingle getInstance(){
            return INSTANCE;
        }
    }
    class Test{
        public static void main(String[] args) throws Exception {
            EnumSingle instance1 = EnumSingle.INSTANCE;
            Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            EnumSingle instance2 = declaredConstructor.newInstance();
            System.out.println(instance1);
            System.out.println(instance2);
        }
    }
    

    它竟然说我现在的这个枚举类中没有空参构造器!

    然后就去源码里分析!

    找到这个class文件!利用javap反编译一下!

    发现这个也显示有一个空参构造,证明这个也不对,用第三方的工具查看!

    利用它再吧class文件生成java文件!

    打开这个java文件

    证明是idea和源码骗了我!

    再次尝试破环!

    // enum 本身就是一个class类
    public enum EnumSingle {
    
        INSTANCE;
        public EnumSingle getInstance(){
            return INSTANCE;
        }
    }
    class Test{
        public static void main(String[] args) throws Exception {
            EnumSingle instance1 = EnumSingle.INSTANCE;
            Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
            declaredConstructor.setAccessible(true);
            EnumSingle instance2 = declaredConstructor.newInstance();
            System.out.println(instance1);
            System.out.println(instance2);
        }
    }
    

    结论:反射无法破环枚举类!

  • 相关阅读:
    2019 SDN上机第7次作业
    2019 SDN上机第六次作业
    2019 SDN上机第5次作业
    SDN课程阅读作业(2)
    2019 SDN上机第4次作业
    2019 SDN阅读作业
    2019 SDN上机第3次作业
    第09组 团队Git现场编程实战
    预习非数值数据的编码方式
    预习原码补码
  • 原文地址:https://www.cnblogs.com/yslss/p/12990783.html
Copyright © 2011-2022 走看看