• 23种设计模式---单例设计模式(精华)


    单例模式

    饿汉式:

    package cn.tedu.single;
    //饿汉式单例
    public class Hungry {
        private Hungry(){
        }
        //会造成空间的浪费,开辟了空间,却没有使用
        private  final static Hungry HUNGRY= new Hungry();
    
        public  static Hungry getInstance(){
            return  HUNGRY;
        }
    }
    

    懒汉式:

    存在多线程并发模式,后面的DCL懒汉式解决并发问题

    package cn.tedu.single;
    //懒汉式
    public class LazyMan {
        private LazyMan(){
            System.out.println(Thread.currentThread().getName()+"ok");
        }
        private  static LazyMan lazyMan;
        //双重检测锁模式的懒汉式单例 DCL懒汉式
        public static LazyMan getInstance() {
            if (lazyMan==null){
                    if (lazyMan==null){
                        lazyMan= new LazyMan();//不是一个原子性操作
                }
            }
            return lazyMan;
            /*
             * 1.分配内存空间
             * 2、执行构造方法,初始化对象
             * 3、把这个对象指向者个空间
             *
             * 123(期望的执行顺序)
             * 132 A(实际的执行顺序)
             *     B //此时lazyMan还没有完成构造
             * */
        }
        ////多线程下会有问题
        public static void main(String[] args) {
            for (int i=0;i<5;i++){
                new Thread(()->{
                    lazyMan.getInstance();
                }).start();
            }
        }
    
    }
    
    

    DCL懒汉式:双重检测锁模式的懒汉式单例

    注意:synchronized 解决并发问题,但是因为lazyMan = new LazyMan();不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过volatil来解决

    • Java 语言提供了volatile和 synchronized两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只串行执行。
    • 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 2;
    package cn.tedu.single;
    //懒汉式
    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;
            /*
             * 1.分配内存空间
             * 2、执行构造方法,初始化对象
             * 3、把这个对象指向者个空间
             *
             * 123(期望的执行顺序)
             * 132 A(实际的执行顺序)
             *     B //此时lazyMan还没有完成构造
             * */
        }
        ////多线程下会有问题
        public static void main(String[] args) {
            for (int i=0;i<5;i++){
                new Thread(()->{
                    lazyMan.getInstance();
                }).start();
            }
        }
    
    }
    

    静态内部类

    package cn.tedu.single;
    //通过内部类
    public class Inner {
        //构造器私有
        private Inner(){
    
        }
        public static Inner getInstance(){
            return InnerSon.inner;
        }
        public static class InnerSon{
            private static Inner inner = new Inner();
        }
    }
    

    单例不安全,反射可以破坏(见注释及main方法中反射破解步骤)

    package cn.tedu.single;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    
    //单例不安全可以通过反射破环
    public class Lazy {
        //设置标记
        private  static  boolean flag =false;
        private Lazy(){
            synchronized (Lazy.class){
                if (flag==false){
                    flag=true;
                }else {
                    throw  new RuntimeException("不要试图使用反射破环单例");//但是可以通过设置标记的布尔值
                }
            }
            System.out.println(Thread.currentThread().getName()+"OK");
        }
        private volatile   static Lazy lazy ;
    
        public static Lazy getLazy() {
            synchronized (Lazy.class){
                if (lazy==null){
                    lazy=new Lazy();
                }
            }
            return lazy;
        }
    
        public static void main(String[] args) throws Exception {
            //        Lazy lazy = Lazy.getLazy();
            Field flag = Lazy.class.getDeclaredField("flag");
            flag.setAccessible(true);
            Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
            declaredConstructor.setAccessible(true);
            Lazy lazy1 = declaredConstructor.newInstance();
            flag.set(flag,false);
            Lazy lazy2 = declaredConstructor.newInstance();
            System.out.println(lazy1);
            System.out.println(lazy2);
            //仍然可以创建多个实例,道高一尺,魔高一丈
    
        }
    }
    
    

    枚举:通过反射破解枚举发现不成功:
    1、普通的反编译会欺骗开发者,说enum枚举是无参构造
    2、实际enum为有参构造(见后面);
    3、通过反射破解枚举会发现抛出异常
    Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at .....

    package cn.tedu.single;
    
    import java.lang.reflect.Constructor;
    
    //enmu是什么?本身也是一个class类
    public enum EnumSingle {
        INSTANCE;
        public EnumSingle getInstance(){
            return INSTANCE;
        }
    }
    class Test{
        public static void main(String[] args) throws Exception {
            EnumSingle instance = EnumSingle.INSTANCE;
            Constructor<EnumSingle> declaredConstructor =
                    EnumSingle.class.getDeclaredConstructor(String.class,int.class);// null  java.lang.NoSuchMethodException:
            declaredConstructor.setAccessible(true);
            EnumSingle instance1 = declaredConstructor.newInstance();
            System.out.println(instance);
            // Cannot reflectively create enum objects
            System.out.println(instance1);
    
        }
    }
    
    

    通过idea和jdk自带的反编译枚举如下:

    在这里插入图片描述

    通过jad反编译枚举的代码如下

    在这里插入图片描述

  • 相关阅读:
    支付宝小程序InputItem清除icon不显示
    win11 激活 wi7 win11 魔兽争霸切换 后无法 回到游戏界面 处理办法
    HJ10 字符个数统计
    iOS 15系统导航栏适配
    HJ4 字符串分隔
    HJ7 取近似值
    [iOS]隐藏导航栏3种方式
    HJ3 明明的随机数
    HJ8 合并表记录
    HJ5 进制转换
  • 原文地址:https://www.cnblogs.com/liqbk/p/13126870.html
Copyright © 2020-2023  润新知