KS1_S11_单例模式

2021-06-13_KS1_S11_单例模式

18、彻底玩转单例模式

需要了解的知识:饿汉式、DCL懒汉式,深究:单例模式、枚举

1、饿汉式单例,代码

  1. // 饿汉式单例
    public class HungrySingleton {
        // 可能会浪费空间
        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 HungrySingleton() {
        }
    
        private final static HungrySingleton HUNGRYSINGLETON = new HungrySingleton();
    
        public static HungrySingleton getInstance() {
            return HUNGRYSINGLETON;
        }
    }
    

2、DCL_懒汉式单例,代码:

1. 普通懒汉单例模式
  1. 懒汉式单例_基础版本:单线程中OK

    // 懒汉式单例_基础版本:单线程中OK
    public class LazyMan_01 {
        
        // 构造器私有化
        private LazyMan_01() {
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象
        private static LazyMan_01 lazyMan_01;
    
        private static LazyMan_01 getInstance() {
            if (lazyMan_01 == null) {
                lazyMan_01 = new LazyMan_01();
            }
    
            return lazyMan_01;
        }
    	/* 懒汉式单例_基础版本:单线程中OK */
        public static void main(String[] args) {
                LazyMan_01 instance1 = LazyMan_01.getInstance();
                LazyMan_01 instance2 = LazyMan_01.getInstance();
                LazyMan_01 instance3 = LazyMan_01.getInstance();
                LazyMan_01 instance4 = LazyMan_01.getInstance();
                LazyMan_01 instance5 = LazyMan_01.getInstance();
    
                System.out.println(instance1.hashCode());
                System.out.println(instance2.hashCode());
                System.out.println(instance3.hashCode());
                System.out.println(instance4.hashCode());
                System.out.println(instance5.hashCode());
        }
    }
    
  2. 懒汉式单例_基础版本:单线程下普通懒汉单例模式OK,但在多线程并发下有问题(暴露问题:但在多线程并发下有问题)

    // 懒汉式单例_基础版本:单线程下普通懒汉单例模式OK,但在多线程并发下有问题(**暴露问题**:但在多线程并发下有问题)
    public class LazyMan_01 {
    
        // 构造器私有化
        private LazyMan_01() {
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象
        private static LazyMan_01 lazyMan_01;
    
        private static LazyMan_01 getInstance() {
            if (lazyMan_01 == null) {
                lazyMan_01 = new LazyMan_01();
            }
    
            return lazyMan_01;
        }
    
        /* 懒汉单例OK:多线程并发下的问题暴露 */
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                new Thread(()->{
                    LazyMan_01.getInstance();
                }).start();
            }
        }
    }
    
2. 懒汉式单例_DCL
  1. 懒汉式单例_DCL(解决问题:多线程并发下的懒汉问题1;暴露问题:但在多线程并发下有指令重排问题)

    懒汉式单例_DCL(**解决问题**:多线程并发下的懒汉问题1**暴露问题**:但在多线程并发下有指令重排问题)
    public class LazyMan_02 {
        // 构造器私有化
        private LazyMan_02() {
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象
        private static  LazyMan_02 lazyMan_02;
        
         /* 
         	不是原子性操作:
               1.分配内存空间
               2.执行构造方法,初始化对象
               3.把这个对象指向这个空间
               123
               321 A——指令重排
                   B——此时插入 lazyManSingleton 还没有完成构造
        */
        // 双层检测锁模式——DCL懒汉懒汉模式
        private static LazyMan_01 getInstance() {
            if (lazyMan_01 == null) {
                lazyMan_01 = new LazyMan_01();
            }
    
            return lazyMan_01;
        }
    
        public static void main(String[] args) {
            for (int i = 0; i < 5; i++) {
                new Thread(()->{
                    LazyMan_02.getInstance();
                }).start();
            }
        }
    }
    
  2. 懒汉式单例_DCL(解决问题:解决多线程并发下指令重排问题)

    // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况
    private volatile static  LazyMan_02 lazyMan_02;
    
  3. 懒汉式单例_DCL(暴露问题:用反射能够破坏单例模式1)

    // 懒汉式单例_DCL(**暴露问题**:用反射能够破坏单例模式1)
    public class LazyMan_03 {
    
        // 构造器私有化
        private LazyMan_03() {
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况
        private volatile static LazyMan_03 lazyMan_03;
    
        // 双层检测锁模式——DCL懒汉懒汉模式
        private static LazyMan_03 getInstance() {
            if (lazyMan_03 == null) {
                synchronized (LazyManSingleton.class) {
                    if (lazyMan_03 == null) {
                        lazyMan_03 = new LazyMan_03();
                    }
                }
            }
            return lazyMan_03;
        }
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            LazyMan_03 instance1 = LazyMan_03.getInstance();
    
            Constructor<LazyMan_03> declaredConstructor = LazyMan_03.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            LazyMan_03 instance2 = declaredConstructor.newInstance();
    
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
            System.out.println(instance1 == instance2);
        }
    }
    
  4. 懒汉式单例_DCL(解决问题:通过在无参构造中加锁,解决反射破坏单例模式1)

    // 构造器私有化
    private LazyMan_03() {
        synchronized (LazyMan_03.class) {
            if (lazyMan_03 != null) {
                throw new RuntimeException("异常:不要试图使用反射破坏");
            }
        }
        System.out.println(Thread.currentThread().getName() +"OK");
    }
    
  5. 懒汉式单例_DCL(暴露问题:用反射能够进一步破坏单例模式2)

    // 懒汉式单例_DCL(**暴露问题**:用反射能够进一步破坏单例模式2)
    public class LazyMan_04 {
        // 构造器私有化
        private LazyMan_04() {
            synchronized (LazyMan_04.class) {
                if (lazyMan_04 != null) {
                    throw new RuntimeException("异常:不要试图使用反射破坏");
                }
            }
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况
        private volatile static LazyMan_04 lazyMan_04;
    
        // 双层检测锁模式——DCL懒汉懒汉模式
        private static LazyMan_04 getInstance() {
            if (lazyMan_04 == null) {
                synchronized (LazyManSingleton.class) {
                    if (lazyMan_04 == null) {
                        lazyMan_04 = new LazyMan_04();
                    }
                }
            }
            return lazyMan_04;
        }
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    
            Constructor<LazyMan_04> declaredConstructor = LazyMan_04.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            LazyMan_04 instance1 = declaredConstructor.newInstance();
            LazyMan_04 instance2 = declaredConstructor.newInstance();
    
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
            System.out.println(instance1 == instance2);
        }
    }
    
  6. 懒汉式单例_DCL(解决问题:用反射能够进一步破坏单例模式2)

    懒汉式单例_DCL(**解决问题**:用反射能够进一步破坏单例模式2public class LazyMan_04 {
    
        private static boolean zhout = false;
    
        // 构造器私有化
        private LazyMan_04() {
            synchronized (LazyMan_04.class) {
                if (zhout) {
                    zhout = true;
                } else {
                    throw new RuntimeException("异常:不要试图使用反射破坏");
                }
            }
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况
        private volatile static LazyMan_04 lazyMan_04;
    
        // 双层检测锁模式——DCL懒汉懒汉模式
        private static LazyMan_04 getInstance() {
            if (lazyMan_04 == null) {
                synchronized (LazyManSingleton.class) {
                    if (lazyMan_04 == null) {
                        lazyMan_04 = new LazyMan_04();
                    }
                }
            }
            return lazyMan_04;
        }
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
            Constructor<LazyMan_04> declaredConstructor = LazyMan_04.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            LazyMan_04 instance1 = declaredConstructor.newInstance();
            LazyMan_04 instance2 = declaredConstructor.newInstance();
    
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
            System.out.println(instance1 == instance2);
        }
    }
    
  7. 懒汉式单例_DCL(暴露问题:用反射能够进二步破坏单例模式3)

    // 懒汉式单例_DCL(**暴露问题**:用反射能够进二步破坏单例模式3)
    public class LazyMan_05 {
    
        private static boolean zhout = false;
    
        public static boolean isZhout() {
            return zhout;
        }
    
        // 构造器私有化
        private LazyMan_05() {
            synchronized (LazyMan_05.class) {
    //            if (lazyMan_05 != null) {
                if (zhout) {
                    throw new RuntimeException("异常:不要试图使用反射破坏");
                } else {
                    zhout = true;
                }
    //            }
            }
            System.out.println(Thread.currentThread().getName() +"OK");
        }
    
        // 提供一个实例,但并不创建对象。volatile:对于同一个变量,在一个线程中值发生了改变,则在另一个线程中立即生效,可以大幅度避免线程重排的问题,但不排除极端情况
        private volatile static LazyMan_05 lazyMan_05;
    
        // 双层检测锁模式——DCL懒汉懒汉模式
        private static LazyMan_05 getInstance() {
            if (lazyMan_05 == null) {
                synchronized (LazyManSingleton.class) {
                    if (lazyMan_05 == null) {
                        lazyMan_05 = new LazyMan_05();
                    }
                }
            }
            return lazyMan_05;
        }
    
        public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
    
            Constructor<LazyMan_05> declaredConstructor = LazyMan_05.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            LazyMan_05 instance1 = declaredConstructor.newInstance();
    
            Field zhout = LazyMan_05.class.getDeclaredField("zhout");
            zhout.setAccessible(true);
            zhout.set(instance1, false);
    
            LazyMan_05 instance2 = declaredConstructor.newInstance();
    
            System.out.println(instance1.hashCode());
            System.out.println(instance2.hashCode());
            System.out.println(instance1 == instance2);
        }
    }
    

3、枚举类的单例

  1. 反射无法破坏枚举的单例

    1. 创建枚举类:

      public enum  EnumSingleton {
      
          INSTANCE;
      
          public EnumSingleton getInstance() {
              return INSTANCE;
          }
      }
      
    2. 创建测试类:报错=java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.()

      class Test{
          public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              EnumSingleton instance = EnumSingleton.INSTANCE;
      
              Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(null);
              declaredConstructor.setAccessible(true);
              EnumSingleton instance2 = declaredConstructor.newInstance();
      
              // java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.<init>()
              System.out.println(instance);
              System.out.println(instance2);
          }
      }
      
    3. javap -p 反编译(查看IDEA的target)

      E:\singleton-model\target\classes\com\zhout\single>javap -p EnumSingleton.class
      Compiled from "EnumSingleton.java"
      public final class com.zhout.single.EnumSingleton extends java.lang.Enum<com.zhout.single.EnumSingleton> {
        public static final com.zhout.single.EnumSingleton INSTANCE;
        private static final com.zhout.single.EnumSingleton[] $VALUES;
        public static com.zhout.single.EnumSingleton[] values();
        public static com.zhout.single.EnumSingleton valueOf(java.lang.String);
        private com.zhout.single.EnumSingleton();
        public com.zhout.single.EnumSingleton getInstance();
        static {};
      }
      
    4. javap -p 反编译结果图

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tcK2sXv-1623586465778)(…/…/KS1_Step11_%E6%BA%90%E7%A0%81%E6%8E%A2%E7%A9%B6and%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/KS1_S11_%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F/KS1_S11_%E5%8D%95%E4%BE%8B%E6%A8%A1%E5%BC%8F.assets/1623572008472.png)]

    5. 最后用专业工具jad进行反编译,发现枚举类的构造函数入参有两个值(String.class,int.class);

    6. 更新测试类:

      class Test{
          public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
              EnumSingleton instance = EnumSingleton.INSTANCE;
      
              Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class,int.class);
              declaredConstructor.setAccessible(true);
              EnumSingleton instance2 = declaredConstructor.newInstance();
      
              // java.lang.NoSuchMethodException: com.zhout.single_zhout.EnumSingleton.<init>()
              System.out.println(instance);
              System.out.println(instance2);
          }
      }
      
    7. 输出报错信息:枚举类不能被反射实例化

      Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
      	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
      
    8. newInstance的源码

      if ((clazz.getModifiers() & Modifier.ENUM) != 0)
                  throw new IllegalArgumentException("Cannot reflectively create enum objects");
      
    9. 小结:反射无法破坏枚举的单例

4、静态内部类

  1. 静态内部类——因为反射也不是安全的

    // 静态内部类——因为反射也不是安全的
    public class Holder {
    
        //  构造器私有化——只要是单例模式
        private Holder() {
    
        }
    
        public static Holder getInstance() {
            return TheInnerClass.HOLDER;
        }
    
        public static class TheInnerClass {
            private static final Holder HOLDER = new Holder();
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值