设计模式 -- 单例模式(多线程)

上一节,我们给出了单例的最简单的实现,这一节我们将解决上一节提出的那个问题----多线程环境下如何运用单例?

解决方案1、同步锁

我们在创建对象之前,先加锁,这样创建对象的那部分部分代码就不会被两个线程执行了。

代码:

public class Singleton
    {
        private static Singleton uniqueInstance;
        private static readonly object syncRoot = new object();//同步锁

        private Singleton() { }

        public static Singleton getInstance()
        {
            //lock确保当一个线程位于临界区时,另外的线程不能进如临界区
            //也就是在同一时刻,加了锁的那部分程序只有一个线程能够进去
            lock(syncRoot)
            {
                if (uniqueInstance == null)
                {
                    uniqueInstance = new Singleton();
                }
            }
            return uniqueInstance;
        }
    }

可以看到,我们唯一做出的修改就是给这个类加了一个同步锁,然后在getInstance方法中创建对象之前先加锁。这很容易理解吧?但是,这种方法是有缺陷的,这里我们用一重锁定来实现多线程的单例,但是由于我们采用同步,每次执行 这个getInstance()方法时都需要判断同步锁,而实际上我们只有在第一次执行时 判断就够了,这也就说这种解决办法会大大降低程序的性能。那么我们在一起寻找其他的更好的解决方案吧。

解决方案2、双重锁

      双重锁就是在创建对象之前加双重锁(实际上三重),首先我们先给创建对象的代码加锁,然后再判断唯一的实例是否已经寻在,如果不存在我们创建对象。

代码:

public static Singleton getInstace()
        {
            //当两个线程同时“冲击”到这一步时,都可以通过判断进入
            if (uniqueInstance == null)
            {
                //这时就只有一个线程可以进入,两一个线程排队等候,必须要一个线程进入并出来后,第二个线程才开可以进入
                lock (syncRoot)
                {
                    //为什么还要继续判断呢?如果没有这个判断 ,当两个线程都冲过第一关,而
                    //第一个线程又创建了实例,放开锁后,那么第二个线程还是可以继续创建实例
                    if (uniqueInstance == null)
                    {
                        uniqueInstance = new Singleton();
                    }
                }
            }
            return uniqueInstance;
        }
    }

第一个 if判断:如果两个线程先后执行到这一步,这时uniqueInstance是null的,那么两个线程都能进入这个判断。
第二个 加锁:这个是给创建对象的代码加锁,防止两个线程同时访问uniqueInstance

第三个 if判断:你可能会疑问为什么还要加一个这个判断?不多余吗?不多于啊。看看代码注视吧。

解决方案3:静态初始化(不推荐使用)

      “静态初始化”方法是由“C#”和“公共语言运行库”提供的,他不需要开发人员显示的编写多线程安全代码,即可解决多线程环境问题。

代码:

public sealed class Singleton//sealed关键字防止该类派生子类,因为派生可能会增加实例
    {
        //在第一次引用类的任何成员时创建实例。公共语言运行库负责处理变量初始化
        private static readonly Singleton uniqueInstance = new Singleton();
        private Singleton() { }
        public static Singleton getInstance()
        {
            return uniqueInstance;
        }
    }

总结:我们可以看到,无论是多线程还是单线程环境下的单例,类的结构都是相同的,首先,类要有一个私有的被类别的实例然后,要有一个私有构造器;最后,提供一个全局访问点。这三点是必不可少的。把握住这三个点来写单例就没问题了。

posted on 2013-04-06 21:00  雨过晴空  阅读(344)  评论(0编辑  收藏  举报

导航