设计模式(十七)单例模式

单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点。通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以被创建,并且它可以提供一个访问该实例的方法。

单例模式因为 Singleton 类封装它的唯一实例,这样它可以严格地控制客户怎样访问它以及何时访问它。简单地说,就是对唯一实例的受控访问。

实用类,如 Math 类,也会采用私有化的构造方法来避免其有实例。但它们还是有很多不同,比如实用类不保存状态,仅提供一些静态方法或静态属性让你使用,而单例类是有状态的。实用类不能用于继承多态,而单例虽然实例唯一,却是可以有子类来继承。实用类只不过是一些方法属性的集合,而单例却是有着唯一的对象实例。

 基本方法

 1 // 
 2 class Singleton
 3 {
 4   private static Singleton instance;
 5 
 6   // 构造方法让其 private,这就堵死了外界利用 new 创建此类实例的可能
 7   private Singleton()
 8   {
 9   }
10  
11   // 此方法是获得本类实例的唯一全局访问点
12   public static Singleton GetInstance()
13   {
14     if(instance == null)
15     {
16       instance = new Singleton();
17     }
18     return instance;
19   }
20 }

 多线程时的单例

为了防止多线程同时创建多个实例,可以给进程加一把锁来处理。lock 是确保当一个线程位于代码的临界区时,另一个线程不进入临界区。如果其他线程试图进入锁定的代码,则它将一直等待(即被阻止),直到该对象被释放。

 1 // 
 2 class Singleton
 3 {
 4   private static Singleton instance;
 5 
 6   // 程序运行时创建一个静态只读的进程辅助对象
 7   private static readonly object syncRoot = new object();
 8 
 9   // 构造方法让其 private,这就堵死了外界利用 new 创建此类实例的可能
10   private Singleton()
11   {
12   }
13  
14   // 此方法是获得本类实例的唯一全局访问点
15   public static Singleton GetInstance()
16   {
17     // 在同一个时刻加了锁的那部分程序只有一个线程可以进入
18     lock(syncRoot)
19     {
20       if(instance == null)
21       {
22         instance = new Singleton();
23       }
24     }
25     return instance;
26   }
27 }

 双重锁定(Double-Check Locking)不用让线程每次都加锁,而只是在实例未被创建的时候再加锁处理。同时也能保证多线程的安全。

 1 // 
 2 class Singleton
 3 {
 4   private static Singleton instance;
 5 
 6   // 程序运行时创建一个静态只读的进程辅助对象
 7   private static readonly object syncRoot = new object();
 8 
 9   // 构造方法让其 private,这就堵死了外界利用 new 创建此类实例的可能
10   private Singleton()
11   {
12   }
13  
14   // 此方法是获得本类实例的唯一全局访问点
15   public static Singleton GetInstance()
16   {
17     // 先判断实例是否存在,不存在再加锁处理
18     if(instance == null)
19     {
20       // 在同一个时刻加了锁的那部分程序只有一个线程可以进入
21       lock(syncRoot)
22       {
23         if(instance == null)
24         {
25           instance = new Singleton();
26         }
27       }
28     }
29     return instance;
30   }
31 }

静态初始化

C# 与公共语言运行库也提供了一种 “静态初始化” 方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决多线程环境下不安全的问题。

 1 // sealed 阻止发生派生,而派生可能会增加实例
 2 class sealed class Singleton
 3 {
 4   // 在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化
 5   private static readonly Singleton instance = new Singleton();
 6 
 7   // 构造方法让其 private,这就堵死了外界利用 new 创建此类实例的可能
 8   private Singleton()
 9   {
10   }
11  
12   // 此方法是获得本类实例的唯一全局访问点
13   public static Singleton GetInstance()
14   {
15     return instance;
16   }
17 }

【总结】

这样的实现与前面的示例类似,也是解决了单例模式试图解决的两个基本问题:全局访问实例化控制

公共静态属性为访问实例提供了一个全局访问点。不同之处在于它依赖公共语言运行库来初始化变量。由于构造方法是私有的,因此不能在类的本身以外实例化 Singleton 类;因此,变量引用的是可以在系统中存在的唯一实例。不过要注意的是,instance 变量标记为 readonly,这意味着只能在静态初始化期间或在类构造函数中分配变量。

由于这种静态初始化的方式是在自己被加载时就将自己实例化,所以被形象地称为饿汉式单例类,原先的单例模式处理方式是要在第一次被引用时,才会将自己实例化,所以就被称为懒汉式单例类

饿汉式,即静态初始化的方式,它是类一加载就实例化的对象,所以要提前占用系统资源。懒汉式单例,会面临多线程访问的安全性问题,需要做双重锁定这样的处理才可以保证安全。

从C#语言角度来讲,饿汉式的单例类已经足够满足我们的需求了。

posted @ 2015-06-20 15:25  壬子木  阅读(106)  评论(0)    收藏  举报