• 设计模式(4) -- 单例模式(Singleton)


    设计模式(4)  -- 单例模式(Singleton)

    试想一个读取配置文件的需求,创建完读取类后通过New一个类的实例来读取配置文件的内容,在系统运行期间,系统中会存在很多个该类的实例对象,也就是说系统中会同时存在多份配置文件的内容,这样会严重浪费内存资源。这样需要实现:在一个系统运行期间,只要一个类实例就可以了。

    单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

    现在一个类能够被创建多个实例,问题的根源在于类的构造方法是公开的,也就是可以让类的外部通过构造方法创建多个实例。换句话说,只要类的构造方法能让类的外部访问,就没有办法去控制外部来创建这个类的实例个数。

    要想控制一个类只被创建一个实例,那么首要的问题就是要把创建实例的权限收回来,让类自身来负责自己类实例的创建工作,然后由这个类来提供外部可以访问这个类实例的方法,这就是单例模式的实现方式。

    在Java中,单例模式分为两种,懒汉式和饿汉式。

    懒汉式:

    public classSingletonLan {

         privatestaticSingletonLan uniqueInstance = null;

         

         privateSingletonLan(){

          

         }

         

         publicstaticsynchronizedSingletonLan getInstance(){

          if(uniqueInstance==null){

              uniqueInstancenew SingletonLan();

          }

          return uniqueInstance;

         }

         

         publicvoidsingletonOPeration(){

          

         }

         

         privateString singletonData;

         

         publicStringgetSingletonData(){

          return singletonData;

         }

    }

    饿汉式:

    public classSingletonE {

        private static SingletonE uniqueInstance= newSingletonE();

       

       privateSingletonE(){

         

       }

       

       publicstaticSingletonEgetInstance(){

         return uniqueInstance;

       }

       

       publicvoidsingletonOPeration(){

         

       }

       

       privateString singletonData;

       

       publicString getSingletonData(){

         return singletonData;

       }

    }

    Java里面实现的单例是一个虚拟机范围。因为装载类的功能是虚拟机的,所以一个虚拟机在通过自己的ClassLoader装载饿汉式实现单例类的时候就会创建一个类的实例。这就意味着如果一个虚拟机里面有很多个ClassLoader,而且这些ClassLoader都装载某个类的话,就算这个类是单例,它也会产生很多个实例。当然,如果一个机器上有多个虚拟机,那么每个虚拟机里面都应该至少有一个这个类的实例,也就是说整个机器上就有很多个实例,更不会是单例了。

    单例模式的精粹是static变量在类装载的时候进行初始化;多个实例的static变量会共享同一块内存区域。

    单例模式有一种应用叫延迟加载(Lazy Load):一开始不需要加载资源或者数据,一直等,等到马上要使用这个资源或者数据的时候才去加载。

    利用缓存来实现单例模式:

    import java.util.HashMap;

    import java.util.Map;

     

    public classSingletonCache {

           private final static String DEFAULT_KEY="One";

          

           private static Map<Object,SingletonCache> map = newHashMap<Object,SingletonCache>();

          

           private SingletonCache(){

           

           }

          

           public static SingletonCache getInstance(){

           SingletonCache instance = (SingletonCache)map.get(DEFAULT_KEY);

           if(instance==null){

               instance = new SingletonCache();

               map.put(DEFAULT_KEY,instance);

           }

           return instance;

           }

    }

    从线程安全上讲,懒汉式是线程不安全的,饿汉式是线程安全的(虚拟机保证只会加载一次,在装载类的时候是不会发生并发的),但是加了synchronized关键字的懒汉式是线程安全的,只是效率下降了。

    可以使用双重检查加锁机制,指的是并不是每次进入getInstance方法都需要同步,而是先不同步,进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了。

    双重检查加锁机制的实现用到关键字volatile,意思是被volatile修饰的变量的值,将不会被本地线程缓存,所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。由于volatile关键字可能会屏蔽虚拟机中一些必要的代码优化,所以运行效率并不是很高。

    public classSingletonVolatile {

         privatevolatilestaticSingletonVolatile instance = null;

         

         privateSingletonVolatile(){

          

         }

         

         publicstaticSingletonVolatilegetInstance(){

          if(instance== null){

               synchronized(SingletonVolatile.class){

                  if(instance==null){

                      instance = newSingletonVolatile();

                  }

              }

          }

          return instance;

         }

    }

    Lazyinitialization holder class模式综合实现了延迟加载和线程安全。在类加载的时候不去初始化对象。

    public classSingletonClass {

        privatestaticclassSingletonHolder{

         private staticSingletonClass instance = newSingletonClass();

        }

        

        privateSingletonClass(){

         

        }

        

        publicstaticSingletonClassgetInstance(){

         return SingletonHolder.instance;

        }

    }

    据说单元素的枚举类型已经成为实现Singleton的最佳方法。

  • 相关阅读:
    正则化方法:L1和L2 regularization、数据集扩增、dropout
    如何估算深度神经网络的最优学习率
    机器学习算法中如何选取超参数:学习速率、正则项系数、minibatch size
    leetcode 389. Find the Difference
    python导入模块
    如何选择正确的激活函数?
    leetcode 690. Employee Importance——本质上就是tree的DFS和BFS
    leetcode 258. Add Digits——我擦,这种要你O(1)时间搞定的必然是观察规律,总结一个公式哇
    leetcode 520. Detect Capital
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(12月21日)
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3180218.html
Copyright © 2020-2023  润新知