经典的double-check locking:
{
private static Singleton instance;
private static readonly Object syncRoot = new Object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
你知道么,这个版本在Java中是否有问题的,在特殊情况下仍然是有问题的(不清楚最新的JVM是否已改进), http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html。问题在于,JVM第一次调用GetInstance方法开始时,将instance读入CPU寄存器。然后,对第二个if求值时,它直接查询寄存器,即null,如果此时恰巧有多个线程调用 GetInstance方法,则会创建多个instance对象。而在C#中,这种方式也不是严格的可靠。按照Jeffrey Richter的说法,CLR在编译instance = new Singleton() 这个地方,有可能是先为instance分配内存区域,然后就publishing,最后才调用私有构造函数。执行完这个过程的第二步(即publishing)时,其他线程若此时调用GetInstance方法,就直接获取到尚未初始化的instance对象了(这个时候它已经不为null,但是尚未初始化)。
改进的办法有以下几种:
1. 使用volatile关键字,微软的实现示例http://msdn.microsoft.com/en-us/library/ff650316.aspx 。代码如下:
{
private static volatile Singleton instance;
private static readonly Object syncRoot = new Object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
instance = new Singleton();
}
}
}
return instance;
}
}
即private static volatile Singleton instance; 。关于volatile ,它用于指示一个字段可以由多个同时执行的线程修改。 声明为 volatile 的字段不受编译器优化(假定由单个线程访问)的限制。 这样可以确保该字段在任何时间呈现的都是最新的值。
这种方式的缺点是,它会对instance的所有属性的读取也需要同步,对性能造成一定程度上的损害。
2. 使用Interlocked:
{
private static Singleton instance;
private static readonly Object syncRoot = new Object();
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
{
var temp = new Singleton();
Interlocked.Exchange(ref instance, temp);
}
}
}
return instance;
}
}
Interlocked.Exchange(ref instance, temp); 用于确保新创建的Singleton对象只有在执行构造函数结束后才publishing到instance中。
3. 缘于CLR对类构造器的调用线程安全性,还有一个简单的方式来实现线程安全的singleton:
{
private static readonly Singleton instance = new Singleton();
private Singleton()
{
}
public static Singleton GetInstance()
{
return instance;
}
}
当然,它有一个问题,就是instance对象可能会在使用前就被创建(当调用Singleton其他静态方法时)。所以就又有了改进版:
{
private Singleton()
{
}
public static Singleton GetInstance()
{
return Nested.Instance;
}
private class Nested
{
internal static readonly Singleton Instance = new Singleton();
}
}
4. 还有一种实现方式:
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton GetInstance()
{
if (instance == null)
{
var temp = new Singleton();
Interlocked.CompareExchange(ref instance, temp, null);
}
return instance;
}
}
var temp = new Singleton() 可能会创建多个对象,但是Interlocked.CompareExchange 确保只有一个对象赋值给instance,其它对象则因没有被引用而将被垃圾回收。这种方式的优点在于,它没有锁。
在Framework4.0中新增了Lazy和LazyInitializer类,实际上就可以更轻易实现singleton了。
{
private static readonly Lazy<Singleton> lazySingleton = new Lazy<Singleton>(() => new Singleton(), LazyThreadSafetyMode.PublicationOnly);
private Singleton()
{
}
public static Singleton GetInstance()
{
return lazySingleton.Value;
}
}
lazySingleton 的实现具有线程安全,并且仅当调用GetInstance 时才会去实例对象。
{
private static Singleton instance;
private Singleton()
{
}
public static Singleton GetInstance()
{
if(instance == null)
{
LazyInitializer.EnsureInitialized(ref instance, () => new Singleton());
}
return instance;
}
}
LazyInitializer 则更方便,使用的是静态方法,干净利落。
源码包,download