设计模式(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的最佳方法。

原文地址:https://www.cnblogs.com/snake-hand/p/3180218.html