zoukankan      html  css  js  c++  java
  • 你未必真正了解的Singleton

            缘于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

  • 相关阅读:
    Spring整合MyBatis(一)MyBatis独立使用
    Spring AOP源码分析(三)创建AOP代理
    Spring AOP源码分析(二)动态A0P自定义标签
    Spring AOP源码分析(一)使用示例
    JDK(十)JDK1.7&1.8源码对比分析【集合】ConcurrentHashMap
    JDK(九)JDK1.7源码分析【集合】HashMap的死循环
    JDK(八)JDK1.7&1.8源码对比分析【集合】HashMap
    MySQL(五)SELECT语句执行顺序
    版本控制器:SVN
    springmvc
  • 原文地址:https://www.cnblogs.com/Langzi127/p/2671356.html
Copyright © 2011-2022 走看看