单例模式

单例模式

一. 意图

      对于某些类来说,我们其实只需要有一个实例化的对象。比如:注册表,资源管理器,打印机驱动程序等等。

如果我们保证以上的类只有一个实例,并只提供一个统一的访问点的话。系统中便可以统一管理这个对象。

以上类只有一个实例,同时也可以节约系统资源,保证对象信息的一致性。

    我们可以通过单例模式来确保对象的唯一性。

二. 定义

      确保某一个类只有一个实例,只提供一个全局访问点,该类自行实例化并向整个系统提供这个实例,这个类就是单例类

    类图:

   

三. 单例模式实现的三个要点

  1. 要保证类只有一个实例,就要禁止类的外部直接使用new来创建对象。把单例类的构造函数的可见性要改为private,只在类的内部使用工厂方法创建实例。
  2. 在单例类中定义一个私有的静态的自己类型的成员变量。
  3. 在单例类中定义一个公开的静态的实例化方法。在实例化方法中判断成员变量 是否实例化。如果没有实例化,则创建一个自身实例。如果已经创建,就直接返回成员变量。

         代码示例(java)

        

四 饿汉式单例与懒汉式单例

     上述的代码示例,在运行多线程的程序中,可能还是存在一些问题,不能保证对象的唯一性。

例如:有线程1和线程2,同时调用Singleton类的Instance方法。线程1中判断_instance字段为null,对_instance字段进行初始化操作。

     如果Singleton类的初始化信息量大,初始化时间较长。在线程1初始化_instance字段的过程中,假设线程2也调用了Singleton类的Instance方法。如果此时_instance字段还是为null的话,线程2也会对_instance字段进行初始化操作。从而产生了两个Singleton类的实例。Singleton类的实例唯一性无法保证。

     对于多线程的问题,我可以使用饿汉和懒汉单例模式来保证对象的唯一性

解决方案:

     1.饿汉式单例

  • 类图

  

  • 代码示例(java)

  • 说明

      当Singleton类加载时,静态变量_instance会被初始化,此时Singleton类的私有构造函数会被调用,单例类的唯一实例就被创建了。

     这样在任何线程调用getInstance()方法之前,Singleton类已经被创建,确保了线程安全。

     2.懒汉式单例

  • 类图

     

  • 代码示例(java)

    

  • 说明
    • 懒汉式单例只有在调用了Instance()方法后,才会实例化对象。并不是类一加载的时候,就实例化单例对象。使用延迟加载技术。
    • 在定义静态变量_instance的时,使用了修饰符volatilevolatile修饰符可确保成员变量_instance在多个线程之间信息同步。但是会降低系统运行的效率。
    • 使用关键字synchronized可以对代码进行了锁定,一个线程在实例化对象的时候,另一个线程就必须等待。可以防止多个线程同时实例化Singleton对象。保证了线程的安全,但是锁定代码降低了系统运行的效率。
    • 使用了双重检查锁定,代码示例对_instance是否为null值,进行了两次判断

         第一个_instance == null的判断,是有性能上的好处的。因为只有在第一次_instance == null的时候,才会有对代码进行锁定的操作。_instance不为null的时候,就直接返回了_instance对象了。这样对代码进行锁定的操作只会进行一次了。

         如果不进行双重判断,还是可能将会产生多个单例对象,从而违背单例模式的设计思想。

         假设有线程A和线程B同时调用了Instance()方法,此时_instance为null,线程A和线程B都能通过instance == null的第一次判断。

         由于实现了synchronized加锁机制,线程A和线程B不能同时执行synchronized锁定的代码。

         假设线程A先进入synchronized锁定的代码,实例化Singleton对象。而线程B则处于排队等待状态,它必须等待线程A执行完毕后,才可以进入synchronized锁定的代码中。

         但当线程A实例化完毕后,此时线程B并不知道Singleton对象已经被线程A实例化完毕了。所以必须在synchronized锁定的代码中加上第二个_instance==null的判断。不然线程B将继续创建新的实例,从而导致产生两个单例对象。

   3.饿汉式单例 vs 懒汉式单例

   饿汉式单例

   优点:

       饿汉式单例在类加载的时候就被实例化,无需考虑多线程的问题。代码也不需要锁定,性能上有一定的优势。

   缺点:

       不管系统中是否要引用实例,饿汉式单例在类加载的时候都会创建对象。如果此时系统不需要引用该实例,这样就会造成系统资源的浪费。 

   懒汉式单例

   优点:

       懒汉式单例实现了延迟加载,在类需要被使用时,才会被实例化,不会一直占用系统资源。

   缺点:

       要处理好多线程同时访问的问题,就需要锁定代码,考虑多个线程的同步,这样就会对系统的性能造成一定的影响。

五.总结

    单例模式是一种比较简单的创建型模式,在某种程度上来说它是限制了而不是促进了类的创建。保证类只有一个实例,并只提供一个全局的访问点。在很多应用软件中都有广泛的应用。

原文地址:https://www.cnblogs.com/YaoxTao/p/4931895.html