• 设计模式之单例模式


    设计模式之单例模式

    1. 概念

    ​ 所谓单例模式,就是采取一定的方法使得系统中只存在某个类的一个实例,并且该类只提供一个获取对象实例的方法(静态方法);比如Hibernate的SessionFactory

    2.实现

    1.静态常量/静态代码块方法(饿汉式)

    类加载的时候就完成了实例化,不存在线程同步问题,但是如果至始至终这个对象都用不到,那就变成了内存的浪费,没有达到Lazy Loading的效果

    步骤:

    • 构造器私有化(外部无法通过new来创建对象)

    • 类的内部创建对象

    • 向外暴露一个公共的getInstance方法

      class Singleton{
      	//1.私有化构造器
          private Singleton(){}
          
          //2.类内部创建对象
          private final static Singleton instance = new Singleton();
          //或者将创建对象放在静态代码块中
          private static Singleton instance ;
          static{
              instance = new Singleton();
          }
          
          //3. 提供一个公共的方法获取对象
          public static Singleton getInstance(){
              return instance;
          }
          
          
      }
      

    2. 懒汉式(Lazy Loading的效果)

    1. 线程不安全

    class Singleton{
    	//1.私有化构造器
        private Singleton(){}
        //2.类内部定义对象
        private  static Singleton instance;   
        //3. 提供一个公共的方法获取对象
        public static Singleton getInstance(){
            if(instance == null){//需要时再创建,不会在类加载的时候创建
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    该方法只能在单线程下执行,多线程下有可能多个线程同时进入到 if 模块,以至于创建出多个实例,实际开发中不能使用这种方法

    2.线程安全,同步方法

    getInstance()方法中加上同步代码关键字synchronized来解决线程问题

        public static synchronized Singleton getInstance(){
            if(instance == null){//需要时再创建,不会在类加载的时候创建
                instance = new Singleton();
            }
            return instance;
        }
    

    效率过低,我们在创建对象实例的时候才需要同步,而这样的代码我们在之后的每次获取实例都会进行一次同步,方法进行同步的效率太低了

    3.双重检查

    volatile可以使共享变量一旦修改就刷新到内存里去,在getInstance方法中,通过synchronized同步来使得线程安全,当多个线程进入外层 if 时,会受到synchronized关键字作用,单个进入到内层 if 中,当instance被创建后,由于volatile的作用会立刻刷新到内存,等待的线程就会在内层 if 中判断false,即保证了单个实例创建,再后来的线程在外层 if 就false了,也解决了效率问题

    class Singleton{
    	//1.私有化构造器
        private Singleton(){}
        //2.类内部定义对象
        private  static volatile Singleton instance;  // volatile可以使共享变量一旦修改就刷新到内存里去
        //3. 提供一个公共的方法获取对象
        public static Singleton getInstance(){
            if(instance == null){//需要时再创建,不会在类加载的时候创建
                synchronized(Singleton.class){
                    if(instance==null){
               			 instance = new Singleton();                    
                    }
                }
            }
            return instance;
        }   
    }
    

    开发中可以使用

    4.静态内部类

    JVM在类加载的时候是线程安全的,并且类加载的时候,其静态内部类不会被加载,所以我们可以利用这一点来达到线程安全和Lazy Loading的效果

    class Singleton{
        //1.对象实例在内部类加载时被创建
        public static class SingletonInstance{
            private static final Singleton INSTANCE = new Singleton();
        }
        
    	//2.私有化构造器
        private Singleton(){} 
        //3. 提供一个公共的方法获取对象,调用该方法时,内部类第一次被加载,创建对象实例
        public static Singleton getInstance(){
            return SingletonInstance.INSTANCE;
        }   
    }
    

    开发中推荐使用

    5.枚举

    借助JDK1.5中添加的枚举来实现单例模式。不仅可以避免多线程同步问题,而且还能防止反序列化重新创建性的对象

    enum Singleton{
    	INSTANCE;
        public void play(){
            //code。。。
        }
    }
    
    //可以直接调用获取
    Singleton instance = Singleton.INSTANCE;
    //枚举中的方法也可以直接调用
    instance.play();
    

    开发中推荐使用

    3.应用场景

    • 需要经常创建、销毁的对象
    • 创建对象耗时过多或损耗资源过多的,但又经常用到的重量级对象
    • 工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等等)

    分析Runtime源码中使用的单例模式

  • 相关阅读:
    c#无边框窗体移动 屏蔽双击最大化
    怎么样让代码都带有注释?
    权限设置相关,利用Microsoft.Win32.Security
    计算几何常用算法概览[转]
    VS 常见快捷键
    关于读取txt文件的分段问题
    ajax 常用方法
    文件以附件形式下载的方法
    半角和全角互换
    在ubuntu 中安装 jsdoc
  • 原文地址:https://www.cnblogs.com/JIATCODE/p/13052496.html
Copyright © 2020-2023  润新知