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 [Lea99] 的技术。而 C# 与公共语言运行库也提供了一种"静态初始化"方法,这种方法不需要开发人员显式地编写线程安全代码,即可解决这些问题。
2. C# 与公共语言运行库提供的"静态初始化"方法
One of the reasons Design Patterns [Gamma95] 避免使用静态初始化的原因之一是,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