zoukankan      html  css  js  c++  java
  • 设计模式(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的最佳方法。

  • 相关阅读:
    upcoj 2169 DP
    hdu3415 单调队列
    hdu4417(树状数组)(线段树)(划分树+二分)
    poj3264 线段树水题
    STL Map hdu1004,1075,1263
    hdu1166线段树水题
    <<<<<<<<<用来存代码哒!!!!>>>>>>>>>>>>
    jQuery
    apache配置php
    linux关机、重启命令
  • 原文地址:https://www.cnblogs.com/snake-hand/p/3180218.html
Copyright © 2011-2022 走看看