在进行多线程编程的时候,多个线程访问同一个资源的时候,会发生竞争。解决竞争的办法通常是给资源加锁,保证一个时间只有一个线程访问资源。

下面讨论在单例模式中锁的应用。

public sealed class Singleton
{
    private Singleton()
    {
    }

    private static Singleton instance = null;
    private static object syncRoot = new object();

    public static Singleton Instance
    {
        get
        {
            if (instance == null)
            {
                lock (syncRoot)
                {
                    if (instance == null)
                    {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
}

这里简单说一下,lock(syncRoot) 获取对象syncRoot的互斥锁,可以简单理解为,当多个线程同时执行到lock的时候,大家排队,一个一个地进行。C#中的lock对应于Java中的synchronized。这里在11行与15行进行了重复检查,有些人认为是没有必要的。因为下面的代码是等效的。

public sealed class Singleton
{
    private Singleton() { }
    private static Singleton instance = null;
    private static object syncRoot = new object();

    public static Singleton Instance
    {
        get
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new Singleton();
                }
            }
            return instance;
        }
    }
}

那么我们就结合实际的情况来分析一下二者的区别,为了说明方便,以上两种情况分别简称为 “双重检查锁”和“单重检查锁”。

1、第一次访问Instance,同时来了10个线程。对于双重检查锁,instance为null,10个线程在这里lock处排队;对于单重检查锁,10个线程在lock处排队。二者是相同的。
2、第二次、第三次…,访问Instance,同时来了10个线程。对于双重检查锁,instance不为null,10个线程不用排队,直接返回instance;对于单重检查锁,10个线程,还必须要在lock处排队。双重检查锁的优点体现出来了:避免了不必要的排队现象。也就是说,双重检查锁的第一重检查,是很必要的,它来保证不必要的排队。

发表评论

电子邮件地址不会被公开。 必填项已用*标注