zoukankan      html  css  js  c++  java
  • 设计模式

    设计模式分类

    1. 创建型模式:
      单例模式、工厂模式、抽象工程模式、建造者模式、原型模式
    2. 结构型模式
      适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式
    3. 行为型模式
      模板方法模式、命令模式、迭代器模式、观察者模式、终结者模式、备忘录模式、揭示其模式、状态模式、策略模式、职责链模式、访问者模式

    单例模式

    1. 核心作用
      保证一个类只有一个实例,并且提供一个访问该实力的全局访问点
    2. 创建场景
      Windows的任务管理器
      Windows的回收站
      项目中读取配置文件的类
      网站的计数器
      应用程序的日志应用
      数据库连接池
      操作系统文件系统
      Application(servlet编程中会涉及)
      Spring中,每个Bean默认是单例模式,Spring容器可以管理
      servlet编程中,servlet也是单例
      Spring MVC框架/struts1框架中,控制器对象也是单例
    3. 优点
      由于单例模式只生成一个实例,减少了系统性能的开销,当一个对象的产生需要比较多的资源时,可以通过应用启动时直接产生一个实例对象,然后永久驻留内存的方式来解决
      单例模式可以在系统设置全举办的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理
    4. 常见的5中单例模式实现方式
      主要:
      饿汉式(线程安全,调用效率高。但是,不能延时加载。)
      懒汉式(线程安全,调用效率不高。但是,可以延时加载。)
      其他:
      双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题。不建议使用)
      静态内部类式(线程安全,调用效率高。可以延时加载)
      枚举单例(线程安全,调用效率高,但不能延时加载,并且可以天然的防止反射和反序列化漏洞!)
    饿汉式(单例对象立即加载)

    特点:线程安全,效率高,但不能延时加载
    三点注意:

    1. 构造器私有
    2. 静态属性直接new,因为是饿汉式非常饿,刚开始就直接new
    3. 获取单例对象的静态方法 不需要synchroniezd
    public class Demo02 {
        private static Demo02 instance = new Demo02(); //饿汉式非常饿,上来就new,因为立即加载了,就没有延时加载的优势
        private Demo02() {};
        //这里不用设置线程同步 因为类加载的时候,是一个天然的线程安全模式,所以线程安全,不需要同步,显然效率高
        public static Demo02 getInstance() {
            return demo02;
        }
    }
    
    懒汉式(单例对象 延时加载)

    特点:线程安全,效率不高,可以延时加载(这就是懒)
    三点注意:

    1. 构造器私有
    2. 静态属性不初始化
    3. 获取单例对象的方法 需要加synchronized
    public class Demo03 {
        private static Demo03 instance; //懒汉式,懒所以不立即加载
        private Demo03() {};
      
        //需要时才new,资源利用率高,但是并发效率不高,用了synchroniezd
        public synchronized static Demo03 getInstance() {
            if (instance == null) {
                instance = new Demo03();
            }
            return instance;
        }
    }
    
    双重检测锁

    因为编译器优化和jvm内存模型问题,不建议使用,在 Java 5.0 之后,使用 volatile 来修饰 singleInstance 实例,就不会产生指令重排序的情况,这样 DCL 也就可以正常工作了。
    但因为有了更加方便与安全的替代方式,DCL 也没有什么特别的优势,便被废弃了。

    public class Demo04 {
        private static Demo04 instance;
        private Demo04() {}
        public static Demo04 getInstance() {
            if (instance == null) {
                Demo04 demo04;
                synchronized (Demo04.class) {
                    demo04 = instance;
                    if (demo04 == null) {
                        synchronized (Demo04.class) {
                            demo04 = new Demo04();
                        }
                    }
                    instance = demo04;
                }
            }
            return instance;
        }
    }
    

    上面代码是视频里面的 因为没有讲解我看不懂
    然后用volatile实现的双重检测锁

    public class Demo05 {
        //加上volatile可以解决编译器的优化问题和cpu缓存
        private static volatile Demo05 instance;
        private Demo05() {}
        public static Demo05 getInstance() {
            if (instance == null) {
                synchronized (Demo05.class) {
                    if (instance == null) {
                        instance = new Demo05();
                    }
                }
            }
            return instance;
        }
    }
    
    静态内部类(也是一种懒加载方式)

    特点:线程安全,调用效率高。可以延时加载
    要点:
    构造器私有
    外部类没有static属性,就不会像饿汉式那样立即加载对象
    只有真正调用getInstance(),才会加载静态内部类。加载类时是线程安全的。instance是static final类型,保证内存中只有一个实例存在,而且只能被赋值一次,从而保证了线程安全性。
    兼备了并发高效调用和延时加载的优势

    //加载类是不回去加载静态内部类,只有调用方法时才会加载,所以是懒加载,也就能延时加载
    public class Demo06 {
        private Demo06() {}
        private static class ClassInstance {
            private static final Demo06 instance = new Demo06();
        }
        public static Demo06 getInstance() {
            return ClassInstance.instance;
        }
    }
    
    枚举实现

    优点:实现简单,枚举本身就是单例模式,由JVM从根本上提供保障,避免通过反射和反序列化的漏洞
    缺点:无延迟加载

    //枚举类
    public enum Demo07 {
        //枚举元素,本身就是单例,可以直接类名调用
        INSTANCE;
        //枚举类还有可以自己的操作
        public void operationMethod() {
            //代码
        }
    }
    
    如何防止反射和反序列漏洞
    1. 解决反射
      在私有构造方法中加入if (instance != null) {throw new RuntimeException}
    public class Demo02 {
        private static Demo02 instance = new Demo02(); //饿汉式非常饿,上来就new
        private Demo02() {
            if (instance != null) {
                throw new RuntimeException();
            }
        };
        //这里不用设置线程同步 因为类加载的时候,是一个天然的线程安全模式
        public static Demo02 getInstance() {
            return instance;
        }
    }
    
    class Main01 {
        public static void main(String[] args) {
            Demo02 demo02_01 = Demo02.getInstance();
            Demo02 demo02_02 = Demo02.getInstance();
            System.out.println(demo02_01);
            System.out.println(demo02_02);
            try {
                Class<Demo02> cla = (Class<Demo02>)Class.forName("单例设计模式.Demo02");
                Constructor<Demo02> constructor = cla.getDeclaredConstructor(null);
                constructor.setAccessible(true);
                Demo02 demo02_03 = constructor.newInstance();
                Demo02 demo02_04 = constructor.newInstance();
                System.out.println(demo02_03);
                System.out.println(demo02_04);
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    1. 反序列化时需要一个方法 private Object readResolve() throws ObjectStreamException {return instance}
    public class Demo03 implements Serializable {
        private static Demo03 instance; //懒汉式,懒所以不立即加载
        private Demo03() {};
    
        //需要时才new,资源利用率高,但是并发效率不高,用了synchroniezd
        public synchronized static Demo03 getInstance() {
            if (instance == null) {
                instance = new Demo03();
            }
            return instance;
        }
    
        private Object readResolve() throws ObjectStreamException {
            return instance;
        }
    }
    
    class Main02 {
        public static void main(String[] args) {
            Demo03 demo03_01 = Demo03.getInstance();
            Demo03 demo03_02 = Demo03.getInstance();
            System.out.println(demo03_01);
            System.out.println(demo03_02);
            try {
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:/users/night/desktop/a.txt"));
                ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:/users/night/desktop/a.txt"));
                objectOutputStream.writeObject(demo03_01);
                Demo03 demo03_03 = (Demo03) objectInputStream.readObject();
                System.out.println(demo03_03);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    
    多线程模式下测试单例模式各个方法的效率

    用countDownLatch测试方法的效率

    public class Demo08 {
        public static void main(String[] args) {
            long start = System.currentTimeMillis();
            int threadNum = 10;
            final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
            for (int i = 0; i < threadNum; i++) {
                new Thread(() -> {
                    for (int j = 0; j < 1000000; j++) {
                        Object instance = Demo07.INSTANCE;
                    }
                    countDownLatch.countDown();
                }).start();
            }
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            long end = System.currentTimeMillis();
            System.out.println(end - start);
        }
    }
    
    
    如何选用?

    单例对象 占用资源少,不需要延时加载
    枚举式好于饿汉式
    单例对象 占用资源大,需要延时记载
    静态内部类好于懒汉式

  • 相关阅读:
    友元函数
    异常处理
    RTTI
    接口类
    纯虚函数和抽象类
    虚函数与虚析构函数原理
    查看表空间使用率及shrink 表空间
    RAC fail over 测试
    js判断数组中是不是有某个元素
    layui 表格图片放大
  • 原文地址:https://www.cnblogs.com/nightrainlemon/p/11654257.html
Copyright © 2011-2022 走看看