Java设计模式-创建型模式-单例模式

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

PS:第一次尝试发博客,如有不足之处,欢迎多多指点!

单例模式(Singleton)

  • 核心作用
  • 保证一个类只有一个实例,并且提供一个访问该实例的全局访问点

常见应用场景

  • 项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件数据,每次new一个对象去读取
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源
  • spring中,每个bean默认就是单例的,这样做的优点是spring容器可以管理
  • 在spring MVC框架/struts1框架中,控制器对象也是单例
  • application 也是单例的典型应用
  • 在servlet编程中,每个servlet也是单例

单例模式的优点

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

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

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

如何选用?

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

饿汉式(单例对象立即加载)

  • 饿汉式单例模式代码中,static变量会在类加载时初始化,此时也不会涉及多个线程对象访问该对象的问题,虚拟机保证只会加载一次该类,肯定不会发生并发访问的问题,因此,可以省略synchronized关键字
    -如果只是加载本类,而不是要调用getSingleton(),甚至永远没有调用,则会造成资源浪费

      class SingletonEHan{
          //类初始化时,立即加载这个对象(没有延时加载的优势,)加载时,天然的是线程安全的
          private static SingletonEHan singleton = new SingletonEHan();
      
      
          //私有化构造器
          private SingletonEHan() {
          }
      
          //方法不同步,调用效率高
          public static SingletonEHan getSingleton() {
              return singleton;
          }
    
      }
    

懒汉式(单例对象延时加载)

  • lazy load 延时加载,懒加载,真正用的时候才加载
    -资源利用率高,但是每次调用getSingleton()方法都要同步,并发效率较低

      class SingletonLHan{
          //类初始化时,不初始化这个对象,延时加载,真正需要时再创建
          private static SingletonLHan singleton;
      
          //私有构造器
          private SingletonLHan() {
          }
      
          //方法同步,调用效率低
          public static synchronized SingletonLHan getSingleton() {
              if (singleton == null){
                  singleton = new SingletonLHan();
              }
              return singleton;
          }
      }
    

双重检测锁模式

  • 这个模式将同步内容下方到if内部,提高了执行的效率,不必每次获取对象时都进行同步,只有第一次才同步,创建了以后就没必要了
    -由于编译器优化原因和JVM底层内部模型原因,偶尔会出问题,不建议使用

      class SingletonDouble{
          private static SingletonDouble singleton;
      
          //私有构造器
          private SingletonDouble(){}
      
          //
          public static SingletonDouble getSingleton(){
              if (singleton == null){
                  SingletonDouble sc;
                  synchronized (SingletonDouble.class){
                      sc = singleton;
                      if (sc == null){
                          synchronized (SingletonDouble.class){
                              if (sc == null){
                                  sc = new SingletonDouble();
                              }
                          }
                          singleton = sc;
                      }
                  }
              }
              return singleton;
          }
      }
    

静态内部类(也是一种懒加载)

  • 外部类没有static属性,则不会像饿汉式那样立即加载对象

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

  • 兼备了并发高效调用和延迟加载的优势

      class SingletonStatic{
          //创建一个静态内部类
          private static class SingletonClass{
              private static final SingletonStatic singleton = new SingletonStatic();
          }
      
      	//调用时,通过return 再创建
          public static SingletonStatic getSingleton(){
              return SingletonClass.singleton;
          }
      
          //私有构造器
          private SingletonStatic(){}
      }
    

枚举单例模式

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

  • 缺点:无延时加载

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

猜你喜欢

转载自blog.csdn.net/weixin_44395637/article/details/92402932