您的位置:1010cc时时彩经典版 > 1010cc时时彩经典版 > 单例设计格局,Java设计情势GOF之单例情势

单例设计格局,Java设计情势GOF之单例情势

发布时间:2019-08-17 23:27编辑:1010cc时时彩经典版浏览(57)

    GOF设计模式之1:单例设计模式,gof设计模式

    1.单例设计模式核心作用:

    保证一个类只有一个实例,并且提供了访问该实例的全局访问点

    核心作用

    作用


    保证一个类只有一个实例,并且向外提供一个访问点。

    Java设计模式GOF之单例模式,java设计模式gof

    一、单例模式(Singleton)

    1、单例模式应用场景:

      ①Servlet
      ②任务管理器
      ③链接池
    1010cc时时彩经典版,  ④Spring中每个 bean 默认是单例
      ⑤网站计数器

    2、单例要求

      ①构造器私有
      ②私有的静态变量
      ③公共的静态的可以访问私有的静态变量的方法

    结论:由结果可以得知单例模式为一个面向对象的应用程序提供了对象惟一的访问点,不管它实现何种功能,整个应用程序都会同享一个实例对象。
    二、单例模式的实现方式

    1、饿汉式
      线程安全、立即加载、资源利用率低、调用效率高

    package cn.com.zfc.gof01.singleton;

    /**
    *
    * @title Singleton01
    * @describe 饿汉式实现单例模式
    * @author 张富昌
    * @date 2017年3月27日上午8:40:02
    */
    public class Singleton01 {

      // 天然的线程安全的,类加载是立即加载这个实例
      private static Singleton01 instance = new Singleton01();

      / 构造器私有化
      private Singleton01() {

      }

      // 方法没有同步,效率比较高
      public static Singleton01 getInstance() {
        return instance;
      }
    }

    2、懒汉式
      线程安全、延迟加载、资源利用率高、调用效率低

    package cn.com.zfc.gof01.singleton;

    /**
    *
    * @title Singleton02
    * @describe 懒汉式实现单例模式
    * @author 张富昌
    * @date 2017年3月27日上午8:45:42
    */
    public class Singleton02 {

      private static Singleton02 instance = null;

      //构造其私有化
      private Singleton02() {
      }

      //公共,同步,静态
      public static synchronized Singleton02 getInstance() {
        if (instance == null) {
          instance = new Singleton02();
        }
        return instance;
      }
    }

    3、双重检索式
      线程安全、延迟加载、资源利用率高、调用效率高、但不稳定

    package cn.com.zfc.gof01.singleton;

    /**
    *
    * @title Singleton03
    * @describe 双重检测锁式实现单例模式(不建议使用)
    * @author 张富昌
    * @date 2017年3月27日上午8:52:59
    */
    public class Singleton03 {

      private static Singleton03 instance = null;

      private Singleton03() {

      }

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

    }

    4、静态内部类式(相比于懒汉式更好)
      线程安全、延迟加载、资源利用率高、调用效率高

    package cn.com.zfc.gof01.singleton;

    /**
    *
    * @title Singleton04
    * @describe 静态内部类式实现单例模式
    * @author 张富昌
    * @date 2017年3月27日上午8:54:40
    */
    public class Singleton04 {

      // 构造器私有化
      private Singleton04() {

      }

      // 静态内部类
      private static class StaticInnerClass {
        private static final Singleton04 INSTANCE = new Singleton04();
      }

      public static Singleton04 getInstance() {
        return StaticInnerClass.INSTANCE;
      }

    }

    5、枚举单例式(相比于饿汉式更好)
      线程安全、立即加载、可以天然的防止反射和反序列化

    package cn.com.zfc.gof01.singleton;

    /**
    *
    * @title Singleton05
    * @describe 枚举式实现单例模式
    * @author 张富昌
    * @date 2017年3月27日上午9:01:59
    */
    public enum Singleton05 {
      // 单例对象
      INSTANCE;

      // 如果要对该单例对象进行额外的操作,则加入方法
      public void singlrtonOperation() {

      }

    }

    三、测试多线程环境下五种创建单例模式的效率

    package cn.com.zfc.gof01.singleton.test;

    import java.util.concurrent.CountDownLatch;

    import cn.com.zfc.gof01.singleton.Singleton05;

    /**
    *
    * @title SingletonTest
    * @describe 测试多线程环境下五种创建单例模式的效率
    * @author 张富昌
    * @date 2017年3月27日上午9:24:58
    */
    public class SingletonTest {
      public static void main(String[] args) throws Exception {

        long start = System.currentTimeMillis();
        int threadNum = 10;
        // A synchronization aid that allows one or more threads to wait until a
        // set of operations being performed in other threads completes.
        // 计数器(一个线程执行完成就减1)
        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 = Singleton01.getInstance();
                // 懒汉式,最慢
                // Object o = Singleton02.getInstance();
                // 双重检测锁式,不稳定,不建议使用
                // Object o = Singleton03.getInstance();
                // 静态内部类
                // Object o = Singleton04.getInstance();
                // 枚举式
                Object o = Singleton05.INSTANCE;
              }

              // 一个线程执行完成就减1
              countDownLatch.countDown();
            }
          }).start();
        }

        // 阻塞
        countDownLatch.await(); // main线程阻塞,直到计数器变为0,才会继续往下执行!

        long end = System.currentTimeMillis();
        System.out.println("总耗时:" (end - start));
      }
    }

    四、什么是线程安全?

      如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

      或者说:一个类或者程序所提供的接口对于线程来说是原子操作,或者多个线程之间的切换不会导致该接口的执行结果存在二义性,也就是说我们不用考虑同步的问题,那就是线程安全的。

    一、单例模式(Singleton) 1、单例模式应用场景: ①Servlet ②任务管理器 ③链接池 ④Spring中每个...

    1.单例设计模式核心作用:

    保证一个类只有一个实例,并且提供了访问该实例的全局访问点

    2.常见应用场景:

    • window的任务管理器
    • 项目中读取配置文件一般也是一个单例模式
    • 数据库连接池的设计也是采用单例模式,因为数据库连接是一种数据库资源
    • 操作系统的文件管理系统,也是单例模式,一个操作系统只能有一个文件系统
    • Application也是单例的应用(Servlet编程或者Android的Application类)
    • 在Spring中,每个bean默认也是单例的,这样的有点儿事Spring容器可以管理
    • 在Servlet编程中每个Servlet也是单例的
    • 在Spring MVC和Struts1框架中控制器对象也是单例
    • 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

    应用场景


    windows中的任务管理器就是一个很典型的单例模式。

    读取配置文件的类一般只new一个对象,没必要在每次读取配置文件时重新new一个对象。

    网站计数器也采用单例模式,否则很难同步

    应用程序的日志文件通常只用一个对象来维护

    数据库链接池的设计也采用单例模式。

    文件系统也是一个单例模式,一个操作系统只能有一个文件系统。

    在Spring中,每个Bean默认是单例的,这样Spring容器易于管理。

    javaweb中的每个Servlet都是一个单例

    2.常见应用场景:

    • window的任务管理器
    • 项目中读取配置文件一般也是一个单例模式
    • 数据库连接池的设计也是采用单例模式,因为数据库连接是一种数据库资源
    • 操作系统的文件管理系统,也是单例模式,一个操作系统只能有一个文件系统
    • Application也是单例的应用(Servlet编程或者Android的Application类)
    • 在Spring中,每个bean默认也是单例的,这样的有点儿事Spring容器可以管理
    • 在Servlet编程中每个Servlet也是单例的
    • 在Spring MVC和Struts1框架中控制器对象也是单例

    3.单例模式的优点

    • 由于单例模式只生产一个对象,减少了系统开销,当一个对象的产生需要的资源比较多的时候,比如读取配置文件、产生其它依赖对象时,则可以在应用启动的时候直接产生一个单例对象,然后永久驻存内存的方式来解决。
    • 单例模式可以在系统设置全局访问点,优化共享资源的访问。例如可以设计一个单例类,负责所有数据表的映射。

     

    优点


    3.单例模式的优点

    • 由于单例模式只生产一个对象,减少了系统开销,当一个对象的产生需要的资源比较多的时候,比如读取配置文件、产生其它依赖对象时,则可以在应用启动的时候直接产生一个单例对象,然后永久驻存内存的方式来解决。
    • 单例模式可以在系统设置全局访问点,优化共享资源的访问。例如可以设计一个单例类,负责所有数据表的映射。

    4.常见5中单例模式的实现方式:

    主要

    饿汉式:线程安全,调用效率高。但是不能延时加载

    懒汉式:线程安全,调用效率不高。但是可以延迟加载

    其它:

    双重检锁式:由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用

    静态内部类式:线程安全,调用效率高,而且可以延迟加载

    枚举单例:线程安全,调用效率高,不可延迟加载

    常见应用场景

     

    因为只产生一个实例,所以减少了系统开销。可以在系统设置全局访问点,优化共享资源访问。

    4.常见5中单例模式的实现方式:

    主要

    饿汉式:线程安全,调用效率高。但是不能延时加载

    懒汉式:线程安全,调用效率不高。但是可以延迟加载

    其它:

    双重检锁式:由于JVM底层内部模型的原因,偶尔会出现问题,不建议使用

    静态内部类式:线程安全,调用效率高,而且可以延迟加载

    枚举单例:线程安全,调用效率高,不可延迟加载

    饿汉式的示例代码:

    public class Singleton01 {
        //类初始化的时候,立即加载这个对象(没有延时加载的优势)。加载类时,是线程安全的
        private static Singleton01 instance = new Singleton01();
        private Singleton01(){}
        //方法没有同步调用效率高
        public static Singleton01 getInstance(){
            return instance;
        }
    }
    

     

    饿汉式单例模式的代码中,static变量会在类装载的时候进行初始化,此时不会涉及到多个线程对象访问该对象的问题。虚拟机会保证只会装载一次该类,肯定不会发生并发访问的问题,因此可以省略synchronized关键字

    问题:如果仅仅是加载本类,而不是要调用getInstance,甚至永远都没有调用,则会造成资源浪费。

    懒汉式的示例代码

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 测试懒汉式单例模式
     4  */
     5 public class Singleton02 {
     6     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
     7     private static Singleton02 instance = null;
     8     private Singleton02(){}
     9     ////方法同步,调用效率低!
    10     public static synchronized Singleton02 getInstance(){
    11         if(instance == null)
    12             instance = new Singleton02();
    13         return instance;
    14     }
    15 }
    

     

    要点:延迟加载,懒加载真正用到的时候才会选择加载

    问题:

    资源利用率高了,但是每次调用getInstance()方法都要同步,并发效率较低。

    双重检锁实现

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 测试DCL(双重检锁)单例模式
     4  * 
     5  */
     6 public class Singleton03 {
     7     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
     8     private volatile static Singleton03 instance = null;
     9     private Singleton03(){}
    10     ////代码块同步,调用效率要比同步方法要快一些,由于JVM的原因在高并发的情况下会出现问题
    11     public static  Singleton03 getInstance(){
    12         if(instance == null){
    13             synchronized (Singleton03.class) {
    14                 if(instance == null)
    15                     instance = new Singleton03();
    16             }
    17         }
    18         return instance;
    19     }
    20 }
    

     

    提高了执行 的效率,不必每次获取对象的时候都要进行同步,只有第一次才会进行同步创建。

    问题:

    由于编译器优化的原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用。但是我们可以在instance前面添加volatile关键字,这样就没问题了。

    静态内部类实现方式:(懒加载方式)

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 静态内部类单例模式
     4  * 这种方式:线程安全,调用效率高,并且实现了延时加载!
     5  */
     6 public class Singleton04 {
     7     private Singleton04(){}
     8     public static  Singleton04 getInstance(){
     9         return Inner.instance;
    10     }
    11     private static class Inner{
    12         private static final Singleton04 instance = new Singleton04();
    13     }
    14 }
    

     

    外部类没有static属性,则不会像饿汉式那样,上来就把对象造出来了

    只有真正调用getInstance才会加载静态内部类。加载类时是线程安全的。instance 是static final类型,保证了内存中只有这样一个实例存在,而且只被赋值一次,从而保证了线程安全性。

    兼并并发高效调用和延迟加载的优势。

    换一句户说:静态内部有具备饿汉式和延迟加载的优势。

    枚举实现单例:

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 枚举式实现单例模式(没有延时加载)
     4  */
     5 public enum Singleton05 {
     6     instance;//这个枚举元素,本身就是单例对象!
     7     public void operation(){
     8         //添加需要的操作
     9     }
    10 }
    

     

    优点:实现简单;枚举本身就是单例。由JVM从根本上提供保障。避免反射和序列化的漏洞

    缺点:无延迟加载

    饿汉式的示例代码:

    public class Singleton01 {
        //类初始化的时候,立即加载这个对象(没有延时加载的优势)。加载类时,是线程安全的
        private static Singleton01 instance = new Singleton01();
        private Singleton01(){}
        //方法没有同步调用效率高
        public static Singleton01 getInstance(){
            return instance;
        }
    }
    

     

    饿汉式单例模式的代码中,static变量会在类装载的时候进行初始化,此时不会涉及到多个线程对象访问该对象的问题。虚拟机会保证只会装载一次该类,肯定不会发生并发访问的问题,因此可以省略synchronized关键字

    问题:如果仅仅是加载本类,而不是要调用getInstance,甚至永远都没有调用,则会造成资源浪费。

    • windows的任务管理器
    • windows的回收站
    • 项目中,读取配置文件的类
    • 网站的计数器,否则难以同步
    • 应用程序的日志应用,一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加
    • 数据库连接池的设计一般也是采用单例,因为数据库连接是一种数据库资源
    • Servlet的Application内置对象
    • Servlet编程中每个servlet也是单例的
    • Spring中每个bean默认就是单例的,这样做的有点是spring容器可以管理
    • spring mvc框架/struts1框架,控制器对象也是单例

    分类


    主要  

    • 饿汉式(线程安全,调用效率高,但不能延时加载)

    • 懒汉式(线程安全,调用效率不高,可以延时加载)

    其它

    • 双重检测锁式 (由于JVM的原因,有时会出现问题)
    • 静态内部类式(线程安全,调用效率高,可以延时加载)
    • 枚举单例(线程安全,调用效率不高,不能延时加载)

    5.如何选用这五种单例模式?

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

    枚举好于饿汉式

    单例对象占用资源大,需要延迟加载

    静态内部类好于懒汉式

    6.问题

    反射可以破解上面(不包含枚举)的实现方式(防止的做法是在构造方法中手动抛出异常)

    反序列化可以破解(不包含枚举)的实现方式

    可以通过定义readResolve防止获得不同对象。反序列化的时候,如果对象所在的类定义了readResolve()方法(一种回调方法),返回自己创建的那个对象。

     

    1.单例设计模式核心作用: 保证一个类只有一个实例,并且提供了访问该实例的全局访问点...

    懒汉式的示例代码

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 测试懒汉式单例模式
     4  */
     5 public class Singleton02 {
     6     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
     7     private static Singleton02 instance = null;
     8     private Singleton02(){}
     9     ////方法同步,调用效率低!
    10     public static synchronized Singleton02 getInstance(){
    11         if(instance == null)
    12             instance = new Singleton02();
    13         return instance;
    14     }
    15 }
    

     

    要点:延迟加载,懒加载真正用到的时候才会选择加载

    问题:

    资源利用率高了,但是每次调用getInstance()方法都要同步,并发效率较低。

    单例模式的优点:

     举例


    • 饿汉式

    一上来就创建对象,立即加载,存在的问题就这个对象可能会一直用不到,白白浪费内存资源

    package com.dy.xidian;
    
    public class SingleDemo1 {
        //类初始化是立即加载对象
        //jvm只会将类加载一次,在加载类时就创建了一个实例
        private static SingleDemo1 instance = new SingleDemo1();
        //构造器私有化,不能被new对象
        private SingleDemo1(){
        }
    
        //对外提供一个访问接口,因为只有一个实例存在,所以线程安全
        public static SingleDemo1 getInstance(){return instance;}
    }
    
    • 懒汉式

    用时才创建对象,延时加载。因为加入了同步机制,所以调用效率不高

    package com.dy.xidian;
    
    public class SingleDemo2 {
        private static SingleDemo2 instance;
        private SingleDemo2() {
    
        }
        // 在调用的时候才会实例化一个对象
        // 存在竞态条件,应进行同步
        public static synchronized SingleDemo2 getInstance() {
            if (instance == null)
                instance = new SingleDemo2();
            return instance;
        }
    }
    

     

    •  双重检测锁实现

    将同步块放到了方法的内部,这样不仅能延时加载,而且提高的调用效率。但是由于编译器优化以及JVM底层内部模型原因,偶而会出现问题,不建议使用package com.dy.xidian;public class SingleDemo3     private static SingleDemo3 instance;

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

     

    • 静态内部类模式

    JVM初始化类SingleDemo4时并不会去初始话其内部内,所以避免了向饿汉式那样立即加载对象。

    只有真正调用getInstance(),才会加载内部类。加载类时是线程安全的,而final能保证内存中只有这样一个实例存在,兼备了高效调用与延迟加载的优势。

    package com.dy.xidian;
    
    public class SingleDome4 {
        private SingleDome4(){}
    
        private static class SingleClassInstance{
            private static final SingleDome4 instance = new SingleDome4();
        }
    
        public static SingleDome4 getInstance(){
            return SingleClassInstance.instance;
        }
    }
    
    • 枚举方式

    枚举类本来就是单例的,但是它不能延时加载

    package com.dy.xidian;
    
    public enum SingleDome4 {
        INSTANCE;
        String a = new String();
        // 处理方法
        public static void main(String[] args) {
            System.out.println(SingleDome4.INSTANCE);
            SingleDome4.INSTANCE.a = "ff";
            System.out.println(SingleDome4.INSTANCE.a);
        }
    }
    

    双重检锁实现

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 测试DCL(双重检锁)单例模式
     4  * 
     5  */
     6 public class Singleton03 {
     7     //类初始化的时候,不初始化这个对象(延时加载,真正用的时候再创建)。
     8     private volatile static Singleton03 instance = null;
     9     private Singleton03(){}
    10     ////代码块同步,调用效率要比同步方法要快一些,由于JVM的原因在高并发的情况下会出现问题
    11     public static  Singleton03 getInstance(){
    12         if(instance == null){
    13             synchronized (Singleton03.class) {
    14                 if(instance == null)
    15                     instance = new Singleton03();
    16             }
    17         }
    18         return instance;
    19     }
    20 }
    

     Volatile关键字的作用:

    • 防止指令重排序如:instance = new Singleton03();这条操作分三步执行,1、分配内存;2、进行初始化;3、将生成对象的堆内存地址赋值给instance变量。这些指令中2、 3的位置可能会进行重排序,导致在获取到对象的时候,该对象还没有进行初始化。volatitle可以防止这种指令进行重排序。
    • 当然Volatile还有一个作用是同步CPU缓存区和内存中的变量

    提高了执行 的效率,不必每次获取对象的时候都要进行同步,只有第一次才会进行同步创建。

    问题:

    由于编译器优化的原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用。但是我们可以在instance前面添加volatile关键字,这样就没问题了。

    • 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决
    • 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理

     存在问题


    单例模式的关键点就是构造器私有化,但是通过反射机制将构造器变成可被外部调用,这样的话单例就会被破坏,对于这种情况解决方案如下。

    package com.dy.xidian;
    
    public class SingleDome4 {
        private static SingleDome4 instance;
            //在构造器中抛出异常
        private SingleDome4() {
            if(instance != null )
                throw new RuntimeException();
        }
    
        public synchronized SingleDome4 getInstance() {
            if (instance == null)
                instance = new SingleDome4();
            return instance;
        }
    }
    

    我们可以通过反序列化的方式产生多个的对象,如果一个单例对象被多次反序列化,那么该对象不再是单例的了,解决方案如下:

    package com.dy.xidian;
    
    import java.io.ObjectStreamException;
    import java.io.Serializable;
    
    public class SingleDome4 implements Serializable{
        private static SingleDome4 instance;
        private SingleDome4() {
            if(instance != null )
                throw new RuntimeException();
        }
    
        public synchronized SingleDome4 getInstance() {
            if (instance == null)
                instance = new SingleDome4();
            return instance;
        }
    
        //基于回调的,在反序列化时该方法会直接被调用,而不是去创建一个新对象
        private Object readResolve() throws ObjectStreamException{
            return instance;
        }
    }
    

      

    静态内部类实现方式:(懒加载方式)

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 静态内部类单例模式
     4  * 这种方式:线程安全,调用效率高,并且实现了延时加载!
     5  */
     6 public class Singleton04 {
     7     private Singleton04(){}
     8     public static  Singleton04 getInstance(){
     9         return Inner.instance;
    10     }
    11     private static class Inner{
    12         private static final Singleton04 instance = new Singleton04();
    13     }
    14 }
    

     

    外部类没有static属性,则不会像饿汉式那样,上来就把对象造出来了

    只有真正调用getInstance才会加载静态内部类。加载类时是线程安全的。instance 是static final类型,保证了内存中只有这样一个实例存在,而且只被赋值一次,从而保证了线程安全性。

    兼并并发高效调用和延迟加载的优势。

    换一句户说:静态内部有具备饿汉式和延迟加载的优势。

    常见的五种单例模式实现方式

     

    枚举实现单例:

     1 package com.chunjiangchao.pattern.singleton;
     2 /**
     3  * 枚举式实现单例模式(没有延时加载)
     4  */
     5 public enum Singleton05 {
     6     instance;//这个枚举元素,本身就是单例对象!
     7     public void operation(){
     8         //添加需要的操作
     9     }
    10 }
    

     

    优点:实现简单;枚举本身就是单例。由JVM从根本上提供保障。避免反射和序列化的漏洞

    缺点:无延迟加载

    主要:

    5.如何选用这五种单例模式?

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

    枚举好于饿汉式

    单例对象占用资源大,需要延迟加载

    静态内部类好于懒汉式

    • 饿汉式(线程安全,调用效率高。但是,不能延时加载)
    • 懒汉式(线程安全,调用效率不高。但是,可以延时加载)

    6.问题

    反射可以破解上面(不包含枚举)的实现方式(防止的做法是在构造方法中手动抛出异常)

    反序列化可以破解(不包含枚举)的实现方式

    可以通过定义readResolve防止获得不同对象。反序列化的时候,如果对象所在的类定义了readResolve()方法(一种回调方法),返回自己创建的那个对象。

    示例代码如下:

     

     1 package com.bjsxt.singleton;
     2 
     3 import java.io.ObjectStreamException;
     4 import java.io.Serializable;
     5 
     6 /**
     7  * 测试懒汉式单例模式(如何防止反射和反序列化漏洞)
     8  *
     9  */
    10 public class SingletonDemo6 implements Serializable {
    11     //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
    12     private static SingletonDemo6 instance;  
    13     
    14     private SingletonDemo6(){ //私有化构造器
    15         if(instance!=null){
    16             throw new RuntimeException();
    17         }
    18     }
    19     
    20     //方法同步,调用效率低!
    21     public static  synchronized SingletonDemo6  getInstance(){
    22         if(instance==null){
    23             instance = new SingletonDemo6();
    24         }
    25         return instance;
    26     }
    27     
    28     //反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象!
    29     private Object readResolve() throws ObjectStreamException {
    30         return instance;
    31     }
    32     
    33 }
    

     测试代码如下:

     1 package com.chunjiangchao.pattern.singleton;
     2 
     3 import java.io.FileInputStream;
     4 import java.io.FileNotFoundException;
     5 import java.io.FileOutputStream;
     6 import java.io.ObjectInputStream;
     7 import java.io.ObjectOutputStream;
     8 import java.lang.reflect.Constructor;
     9 
    10 /**
    11  * 测试单例模式
    12  */
    13 public class SingletonDemo {
    14     public static void main(String[] args) throws Exception {
    15         Singleton01 singleton01 = Singleton01.getInstance();
    16         Singleton01 singleton02 = Singleton01.getInstance();
    17         System.out.println(singleton01.hashCode());
    18         System.out.println(singleton02.hashCode());
    19         
    20         /**
    21         //测试反射
    22         try {
    23             Class<?> clazz = Class.forName("com.chunjiangchao.pattern.singleton.Singleton06");
    24             Constructor<?> constructor = clazz.getDeclaredConstructor(null);
    25             constructor.setAccessible(true);
    26             Singleton06 singleton06 = (Singleton06) constructor.newInstance(null);
    27             System.out.println(singleton06);
    28             System.out.println(Singleton06.getInstance());
    29         } catch (Exception e) {
    30             e.printStackTrace();
    31         }
    32         //打印的结果:
    33         //com.chunjiangchao.pattern.singleton.Singleton06@495c83b2
    34         //com.chunjiangchao.pattern.singleton.Singleton06@58ca40be
    35          */
    36         //通过序列化获取一个对象
    37         Singleton06 instance = Singleton06.getInstance();
    38         FileOutputStream fos =  new FileOutputStream("/Users/xxx/Desktop/a.txt");
    39         ObjectOutputStream oos = new ObjectOutputStream(fos);
    40         oos.writeObject(instance);
    41         oos.close();
    42         fos.close();
    43         
    44         FileInputStream fis = new FileInputStream("/Users/xxx/Desktop/a.txt");
    45         ObjectInputStream ois = new ObjectInputStream(fis);
    46         Object obj = ois.readObject();
    47         ois.close();
    48         fis.close();
    49         System.out.println(instance);
    50         System.out.println(obj);
    51     }
    52 
    53 }
    

    其他:

     7、测试几种模式的效率

    五种单例模式在多线程环境下效率测试:使用CountDownLatch同步工具类,允许当前线程等待其它一组线程都执行完毕后,执行当前线程的后续操作。

    countDown()当前线程执行此方法,计数器-1

    await()调动此方法会一直阻塞当前线程,知道计数器为0的时候重新运行当前线程

    下面示例代码来演示当前线程执行的效率:

     1 package com.chunjiangchao.pattern.singleton;
     2 
     3 import java.util.concurrent.CountDownLatch;
     4 
     5 /**
     6  * 五种单例模式的性能测试
     7  *
     8  */
     9 public class SingletonDemo02 {
    10 
    11     public static void main(String[] args) throws InterruptedException {
    12         int threadNum = 10;
    13         long beginTime = System.currentTimeMillis();
    14         final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
    15         for (int i = 0; i < threadNum; i  ) {
    16             new Thread(new Runnable() {
    17                 
    18                 @Override
    19                 public void run() {
    20                     for (int j = 0; j < 20000; j  ) {
    21                         //Singleton01.getInstance();//18ms
    22                         //Singleton02.getInstance();//49ms
    23                         //Singleton03.getInstance();//22ms
    24                         //Singleton04.getInstance();//32ms
    25                         Singleton05 instance = Singleton05.instance;//9ms
    26                     }
    27                     countDownLatch.countDown();
    28                 }
    29             }).start();
    30         }
    31         //让主线程进行阻塞
    32         countDownLatch.await();
    33         long endTime = System.currentTimeMillis();
    34         System.out.println(endTime-beginTime);
    35     }
    36 
    37 }
    

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

    • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出现问题,不建议使用)
    • 静态内部类式(线程安全,调用效率高。但是,可以延时加载)
    • 枚举单例(线程安全,调用效率高,不能延时加载)

    本文由1010cc时时彩经典版发布于1010cc时时彩经典版,转载请注明出处:单例设计格局,Java设计情势GOF之单例情势

    关键词: