代码改变世界

单例模式完整解析

2017-06-04 17:02  Dirichlet  阅读(261)  评论(0编辑  收藏  举报

volatile, static readonly, double check

直到对象要求产生一个实例才执行实例化;这种方法称为"懒实例化"。懒实例化避免了在应用程序启动时实例化不必要的 singleton

1. 

using System; 
public class Singleton 
{ 
   private static Singleton instance; 
   private Singleton() {} 
   public static Singleton Instance 
   { 
      get  
      { 
         if (instance == null) 
         { 
            instance = new Singleton(); 
         } 
         return instance; 
      } 
   } 
}

但是,这种实现的主要缺点是在多线程环境下它是不安全的。如果执行过程的不同线程同时进入 Instance 属性方法,那么可能会创建多个 Singleton 对象实例。每个线程都会执行下列语句,并决定必须创建新的实例:

if (instance == null) 

解决此问题的方法有很多。一种方法是使用被称为 Double-Check Locking 的技术。而 C# 与公共语言运行库也提供了一种"静态初始化"方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决这些问题。

 

2.  C# 与公共语言运行库提供的"静态初始化"方法

One of the reasons Design Patterns 避免使用静态初始化的原因之一是,C++ 规范在静态变量的初始化顺序方面留下了一些多义性。幸运的是,.NET Framework 通过其变量初始化处理方法解决了这种多义性:

public sealed class Singleton 
{ 
   private static readonly Singleton instance = new Singleton(); 
   private Singleton(){} 
   public static Singleton Instance 
   { 
      get  
      { 
         return instance;  
      } 
   } 
} 

 

3. Double-Check Locking方法

用volatile修饰instance, 只有实例变量instance分配完,其他线程才能访问它,多线程读它的时候要么是null,要么是完整的分配好的对象。

lock的对象是private的object对象,不要lock公有的对象比如this,type,以免发生死锁。

using System; 
public sealed class Singleton 
{ 
   private static volatile Singleton instance; 
   private static object syncRoot = new Object(); 
   private Singleton() {} 
   public static Singleton Instance 
   { 
      get  
      { 
         if (instance == null)  
         { 
            lock (syncRoot)  
            { 
               if (instance == null)  
                  instance = new Singleton(); 
            } 
         } 
         return instance; 
      } 
   } 
}

此 double-check locking 方法解决了线程并发问题,同时避免在每个 Instance 属性方法的调用中都出现独占锁定。这里的避免独占锁定是不需要锁定整个Instance属性方法,性能不受影响。它还允许您将实例化延迟到第一次访问对象时发生。实际上,应用程序很少需要这种类型的实现。大多数情况下,静态初始化方法已经够用。

优点:Double-Check Locking 技术已在公共语言运行库中正确实现。但是其他环境中还是会有一些常见的、与使用 Double-Check Locking 有关的问题。

 

总结:

最后还是推荐用"静态初始化"方法实现Singleton。实现简单,实现依赖于CLR框架解决多线程问题。

 

参考文章:

http://msdn.microsoft.com/zh-cn/library/ff650316.aspx

http://msdn.microsoft.com/zh-cn/library/ms954629.aspx

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html