zoukankan      html  css  js  c++  java
  • 曾经天真的以为单例只有懒汉和饿汉两种!原来单例模式还能被破解!!!

    01-单例设计模式

    第一章:单例模式核心作用

    (1)保证一个类只能有一个实例(一个对象)

    (2)并且提供一个供外界访问该实例的全局访问点

    第二章:常见应用场景

    (1)windows的任务管理器、回收站

    (2)项目中,读取配置文件的类,一般只有一个对象。没必要每次使用配置文件的数据都要new一个对象去读取

    (3)网站的计数器,一般采用单例模式设计,否则无法做到同步

    (4)数据库的连接池设计,一般使用单例模式设计

    (5)在spring中,每个bean默认就是单例模式,这样做的优点是spring容器方便管理

    (6)在servlet编程中,每个Servlet也是单例模式

    第三章:单例模式的优点

    (1)减少系统性能的开销:当一个对象的产生需要比较多的资源时,如需要读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久存留在内存中的方式来解决。

    (2)可以在系统设置全局的访问点。优化共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

    第四章:常见的单例模式实现方式及优缺点

    4.1:饿汉式(重点)

    特点:线程安全、调用效率高;但是,不能延时加载

    在系统启动的时候,加载该类的时候,由于static的原因,会立即去加载该类的实例化,同时也由于是static,该类在内存中只有这一个,达到单例的要求。但是,由于是立即加载,会占用系统存储空间,有一定的缺陷。

    由于将无参构造器私有化,所有外界想要使用该类,必须通过提供的全局唯一访问点,拿到该类的实例化对象。不管外界获取了多少次对象,在内存中,该类的实例对象只有一个。

    public class SingletonDemo02 {
        private static /*final*/ SingletonDemo02 s = new SingletonDemo02();
        private SingletonDemo02(){} // 私有化构造器
        public static /*synchronized*/ SingletonDemo02 getInstance(){
            return s;
        }
    }

    4.2:懒汉式(重点)

    特点:线程安全、调用效率不高;但是,可以延时加载

    在系统启动的时候,加载该类时,并不会立即去初始化该类;

    而是在调用该类的getInstance()方法时,判断当前类是否被创建,如果没有被创建,则进行创建对象,返回。如果已经创建了,直接返回对象。

    同时,考虑到并发的情况下,需要使用synchronized,保证每次只有一个线程去访问该类的方法。保证实例化对象的唯一性。

    这种方式,属于延迟加载,真正用到该类的时候才会去加载。提高了资源利用率,但是调用getInstance()方法都要同步,并发效率低下

    public class SingletonDemo01 {
        private static SingletonDemo01 s;
        private SingletonDemo01(){} // 私有化构造器
        public static synchronized SingletonDemo01 getInstance(){
            if(s==null){
                s = new SingletonDemo01();
             }
            return s;
        }
    }

    4.3:双重检测锁式(了解)

    特点:由于JVM底层内部模型原因,偶尔会出问题,不建议使用

    这个模式将同步内容下方到if内部,提高了执行的效率不必每次获取对象时都进行同步,只有第一次才同步创建了以后就没必要了。

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

    4.4:静态内部类式(理解)

    特点:线程安全、调用效率高;但是,可以延时加载

    将实例化操作放到静态内部类中,外部类没有static,所以,在初始化类的时候,不会立即去加载静态内部类,当然也不会去实例化对象,达到了延迟加载的特性

    只有在调用了getInstance()方法的时候,才会去加载静态内部类。加载类的时候,线程是安全的

    instance是static final修饰的,保证了全局唯一性,即单例性。

    兼备并发高效率调用和延迟加载特性。

    public class SingletonDemo04 {
        private static class SingletonClassInstance {
            private static final SingletonDemo04 instance = new SingletonDemo04();
            }
        public static SingletonDemo04 getInstance() {
            return SingletonClassInstance.instance;
            }
        private SingletonDemo04() {
            }
    }

    4.5:枚举单例(理解)

    特点:线程安全、调用效率高,不能延时加载

    枚举本身就是单例模式。由JVM从根本上提供保障!避免通过反射和反序列化的漏洞!

    public enum SingletonDemo05 {
        /**
        *  定义一个枚举的元素,它就代表了Singleton 的一个实例。
        */
        INSTANCE;
        /**
        *  单例可以有自己的操作
        */
        public void singletonOperation(){
            // 功能处理
        }
    }

    第五章:通过反射和反序列化破解单例模式(除枚举单例)

    5.1:通过反射破解单例模式(除枚举单例)

    //通过反射的方式直接调用私有构造器
    Class<SingletonDemo6> clazz = (Class<SingletonDemo6>) Class.forName("com.bjsxt.singleton.SingletonDemo6");
    Constructor<SingletonDemo6> c = clazz.getDeclaredConstructor(null);
    c.setAccessible(true);
    SingletonDemo6  s3 = c.newInstance();
    SingletonDemo6  s4 = c.newInstance();
    System.out.println(s3);
    System.out.println(s4);

    如何避免反射破解单例?

    通过在构造方法中手动抛出异常

    private SingletonDemo6(){ //私有化构造器
            if(instance!=null){
                throw new RuntimeException();
            }
        }

    5.2:通过反序列化破解单例模式(除枚举单例)

    //通过反序列化的方式构造多个对象 
    FileOutputStream fos = new FileOutputStream("d:/a.txt");
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(s1);
    oos.close();
    fos.close();
    
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
    SingletonDemo6 s3 =  (SingletonDemo6) ois.readObject();
    System.out.println(s3);

    如何避免反序列化破解单例模式?

    表示:在反序列化的时候,如果已经定义了readResolver()方法,则直接返回此方法指定的对象,而不需要单独再创建新对象。

    private Object readResolve() throws ObjectStreamException {
            return instance;
        }

    第六章:五种单例模式的效率问题

    6.1:五种单例模式在多线程环境下的测试效率(相对效率)

     

    CountDownLatch类:

    同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或者多个线程一直等待。

    countDown():当前线程调用此方法,则计数减一,减一放在finally中执行

    await():调用此方法会一直阻塞当前线程,直到计数器为0;

    public static void main(String[] args) throws Exception {
            
            long start = System.currentTimeMillis();
            int threadNum = 10;
            final CountDownLatch  countDownLatch = new CountDownLatch(threadNum);
            
            for(int i=0;i<threadNum;i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        
                        for(int i=0;i<1000000;i++){
    //                        Object o = SingletonDemo4.getInstance();
                            Object o = SingletonDemo5.INSTANCE;
                        }
                        
                        countDownLatch.countDown();
                    }
                }).start();
            }
            
            countDownLatch.await();    //main线程阻塞,直到计数器变为0,才会继续往下执行!
            
            long end = System.currentTimeMillis();
            System.out.println("总耗时:"+(end-start));
        }

    第七章:五种单例模式的选择

    (1)单例对象,占用资源少,不需要延迟加载

    枚举式优于饿汉式

    (2)单例对象,占用资源大,需要延时加载

    静态内部类式优于懒汉式

  • 相关阅读:
    [转]ion-slide-box
    [转]Ionic 实现双击返回键退出功能
    [转]highcharts图表入门之:如何让highcharts图表自适应浏览器窗体的大小或者页面大小
    [转]ionic Accordion list three levels
    [转]ionic $state.go passed $stateParams
    [转]ionic tab view hide tab bar
    [转]Ionic + AngularJS angular-translate 国际化本地化解决方案
    [转]Ionic Datepicker
    [转]通过AngularJS directive对bootstrap日期控件的的简单包装
    [转]轻松学习Ionic (四) 修改应用图标及添加启动画面(更新官方命令行工具自动生成)
  • 原文地址:https://www.cnblogs.com/ncl-960301-success/p/11285517.html
Copyright © 2011-2022 走看看