缘于http://www.cnblogs.com/TomXu/archive/2011/12/19/2291448.html 这篇文章,发现胖哥对于singleton的分析仍然不是十分深入,借鉴CLR via C#,再次深入完美一下singleton。 

   

       经典的double-check locking: 

    public class Singleton
    {
        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 。代码如下:

public class Singleton

    {
        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:

       public class Singleton

    {
        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:

      public class Singleton

    {
        private static readonly Singleton instance = new Singleton();

        private Singleton()
        {
        }

        public static Singleton GetInstance()
        {
            return instance;
        }
    }

当然,它有一个问题,就是instance对象可能会在使用前就被创建(当调用Singleton其他静态方法时)。所以就又有了改进版: 

     public class Singleton

    {
        private Singleton()
        {
        }

        public static Singleton GetInstance()
        {
            return Nested.Instance;
        }

        private class Nested
        {
            internal static readonly Singleton Instance = new Singleton();
        }
    }

 

        4. 还有一种实现方式:

     public class Singleton

    {
        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了。

      public class 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 时才会去实例对象。

     public class Singleton  

    {
        private static Singleton instance;

        private Singleton()
        {
        }

        public static Singleton GetInstance()
        {
            if(instance == null)
            {
                LazyInitializer.EnsureInitialized(ref instance, () => new Singleton());
            }
            
            return instance;
        }
    }

 LazyInitializer 则更方便,使用的是静态方法,干净利落。

 

 源码包,download