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

      一、设计模式

      今天开始学习总结设计模式,首先GOF23种设计模式是国外“F4”总结的“经验之谈”,它其实是一种思想,用以在实际开发中对项目构建的一种参考,在一定程度上使用设计模式能够对程序有很大的提升,提升程序的性能、扩展性、可维护行等等,但是也要避免陷入“过度设计”的局面,这样就适得其反了,所以掌握好真正的精髓和思想就尤为重要了。

      二、单例模式

      都说单例模式是设计模式中最简单的几种设计模式之一,但是感觉要理解每一步的细节也并没有那么简单。

    • 目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。减少系统不断创建、销毁该对象的性能开销。
    • 做法:私有化构造方法,让该类自己提供实例,外部访问全局访问点,有实例则返回,无则创建。同时需要确保线程安全。

      单例模式的实现有多种方式:懒汉式、饿汉式、双检锁式、静态内部类式(登记式)、枚举式,下面来一一说明这些实现方式的优缺点及实现的代码:

    1. 懒汉式
      public class Singleton1 {
      
          private static Singleton1 instance;
          
          //私有化构造函数
          private Singleton1() {}
          
          public static synchronized Singleton1 getIntance() {
              
              if(instance==null) {
                  instance = new Singleton1();
              }
              
              return instance;
          }
      }

      所谓懒汉式,主要特点就是对对象进行懒加载,也叫延迟加载,即在调用该类的getInstance方法时再进行创建对象。优点是在需要该实例的时候再进行创建,减少内存开销,因为作为单例对象,在创建的过程中往往需要消耗比其他对象更多的系统资源。缺点是调用的效率会低,因为创建对象需要一定的时间,并且为了保证线程安全加了synchronized关键字进行加锁,也降低了时间效率。

    2. 饿汉式
      /**
       * 单例模式之饿汉式 :线程安全(jvm保证在类加载过程只产生一个对象),
       * 调用效率高,但不是延迟加载
       * @author Administrator
       *
       */
      public class Singleton2 {
      
          private static Singleton2 instance = new Singleton2();
          
          //私有化构造函数
          private Singleton2() {
      
          }
          public static Singleton2 getInstance() {
          
              return instance;
          }
      }

      饿汉式在类声明对象属性的过程中就进行了创建对象,根据classLoader机制,保证了线程安全只会创建一个实例。可以看到饿汉式的实现过程相对简单且线程安全,但是并没有延迟加载。

    3. 双检锁(双重检验锁)
      /**
       * 单例模式之双检锁:线程安全、延迟加载,但调用效率低
       * @author Administrator
       *
       */
      public class Singleton3 {
      
          private static volatile Singleton3 instance;
          
          private Singleton3() {}
          
          public static Singleton3 getInstance() {
              
              if(instance==null) {//可能同时有多个线程进入到该语句,在此处等待进入
                  synchronized(Singleton3.class) {
                      if(instance==null) {//二次校验,可能上一个获取锁的线程已经创建好了实例,故不需要再次创建
                          instance = new Singleton3();
                      }
                  }
              }
              return instance;
          }
      }

      首先我们可以看到,双检锁是属于懒汉式的一种升级版,因为将synchronized关键字的范围缩小了,不必每次调用getInstance方法都需要同步,提高了调用时的效率。实现细节上可看代码注释。

    4. 静态内部类式
      /**
       * 单例模式之静态内部类实现:线程安全,延迟加载,调用效率高
       * @author Administrator
       *
       */
      public class Singleton4 {
      
          private static class SingletonHolder{
              private static final Singleton4 instance = new Singleton4();
          }
          
          private Singleton4() {}
          
          public static final Singleton4 getInstance() {
              
              return SingletonHolder.instance;
          }
      }

      静态内部类应该是前面几种方式中的最佳实践,它既通过classLoader机制保证了实例化对象时的线程安全,跟饿汉式相比,它还具有延迟加载的功能,我们知道饿汉式在类被装载时就会就会初始化静态的变量和方法,而静态内部类的方式只有显示的调用getInstance方法后才会实例化单例对象,所以做到了懒加载。我们需要注意的是,往往实例化我们要的单例对象是很消耗系统资源的,所以我们想在我们想要实例化的时候在进行实例化,而不是类被装载、类调用其他静态方法时而实例化单例对象,所以使用静态内部类的方式更加合理。

    5. 枚举式
      public enum Singleton5 {
      
          //该实例代表单例对象
          INSTANCE;
          
          //单例对象可以有自己的操作
          public void SingletonOperation() {
              //自己的操作
          }
          
      }

      这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
      这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。不过,由于 JDK1.5 之后才加入 enum 特性,用这种方式写不免让人感觉生疏,在实际工作中,也很少用。
      不能通过 reflection attack 来调用私有构造方法。

        从上面的这段话中我们知道,单例模式的常用实现方式存在着安全漏洞,即通过反射机制和反序列化可以实例化多个不同的对象。那我们该如何去防止和处理这两个漏洞呢?我们的做法是首先实现序列化接口,然后再通过readResolve方法返回我们的实例,这样可以解决反序列化问题。通过再私有的构造方法中判断对象是否存在,已存在则抛异常的方式解决反射的问题:以下是静态内部类实现的方法解决相应问题

    public class Singleton4 implements Serializable{
    
        private static class SingletonHolder{
            private static final Singleton4 instance = new Singleton4();
        }
        
        private Singleton4() {
            if(SingletonHolder.instance!=null) {
                throw new RuntimeException("can only create one object");
            }
        }
        
        public static final Singleton4 getInstance() {
            
            return SingletonHolder.instance;
        }
        
        public Object readResolve() throws Exception{
            return SingletonHolder.instance;
        }
    }

    具体的细节可以参考该博客:https://blog.csdn.net/hardwin/article/details/51477359

      以上就是我总结的设计模式之单例模式,如果有写的不对的地方,欢迎评论指出。

  • 相关阅读:
    负载均衡算法(四)IP Hash负载均衡算法
    负载均衡算法(二)加权轮询负载均衡算法
    负载均衡算法(一)轮询负载均衡算法
    移动端媒体查询CSS3适配规则
    JavaScript中label语句的使用
    NODE简易综合应用服务器搭建
    ES6--不定参数
    ES6--默认参数表达式,参数变动
    node基础学习——http基础知识-02-http响应数据流
    node基础学习——http基础知识-01-客户单请求
  • 原文地址:https://www.cnblogs.com/ring2/p/11125741.html
Copyright © 2011-2022 走看看