About double-checked locking

原文链接:http://www.cnblogs.com/zhy2002/archive/2008/11/27/1342181.html

知道double-checked locking的时候应该使用volatile,但是没有仔细想过为什么。今天想起这个问题,费解了一上午,现在终于想到一个解释。

//broken double-checked locking

About double-checked lockingAbout double-checked lockingCode
public class Singleton
{
    private static readonly object syncRoot = new object();
    private static Singleton instance;    
    private readonly string message;

    private Singleton(string message)
    {
        this.message = message;
    }

    public string Message
    {
        get { return message; }
    }

    public static Singleton Instance
    {
        if (instance == null)
        {
            lock (syncRoot)
            {
                if (instance == null)
                {
                    instance = new Singleton("Hello World");  // memory barrier required
                }
            }
        }
        return instance;
    }
}

class Program
{
    static void Main()
    {
        Console.WriteLine(Singleton.Instance.Message);
    }
}

这个例子在ia64上会有问题。因为ia64支持写reorder,因此Singleton构造函数中对message字段的赋值有可能被调整到lock范围中对instance静态字段的赋值之后。因此可能存在这样一个时刻,instance已经保存了一个Singleton实例的引用。但是这个引用的message字段还保持为null。当然这个时刻必然是处于lock的范围内,因为Monitor.Exit具有释放语义,在其他线程Enter之前,instance静态字段和message实例字段就都已经被更新到内存当中。

我今天上午一直费解的是,既然这个不一致状态是在lock之内,为什么会对其他线程有影响?难道会有sb不加锁访问instance么?刚才忽然抬头看到了double-checked 中lock之前的check...

在第一个线程处于lock中的时候,有可能内存中的instance静态字段已经被赋值,但是内存中的message实例还没有被赋值。这个时候第二个线程粉墨登场,虽然第一个线程还持有锁,但由于double-checked中的第一个check不加锁,因此看到instance已经初始化好了,于是就获得了单例。然后第二个线程访问message字段的时候发现居然是null耶,what the hell...

我想这个就是ia64中double-checked locking is broken的原因。一个reorder + 不加锁访问对象造成的错误。

 

转载于:https://www.cnblogs.com/zhy2002/archive/2008/11/27/1342181.html

上一篇:使用PHP锁定MYSQL表


下一篇:对象锁定私有类成员 – 最佳实践? (JAVA)