有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

这个图案的名字?(答:带双重检查锁定的延迟初始化)

考虑下面的代码:

public class Foo
{
    private static object _lock = new object();

    public void NameDoesNotMatter()
    {
        if( SomeDataDoesNotExist() )
        {
            lock(_lock)
            {
                if( SomeDataDoesNotExist() )
                {
                    CreateSomeData();
                }
                else
                {
                    // someone else also noticed the lack of data.  We
                    // both contended for the lock.  The other guy won
                    // and created the data, so we no longer need to.
                    // But once he got out of the lock, we got in.
                    // There's nothing left to do.
                }
            }
        }
    }

    private bool SomeDataDoesNotExist()
    {
        // Note - this method must be thread-safe.
        throw new NotImplementedException();
    }

    private bool CreateSomeData()
    {
        // Note - This shouldn't need to be thread-safe
        throw new NotImplementedException();
    }
}

首先,我需要说明一些假设:

  1. 有一个很好的理由,我不能在应用程序启动后就这么做。可能数据还不可用,等等。

  2. Foo可以从两个或多个线程实例化并并发使用。我希望其中一个最终创建一些数据(但不是两个),然后我将允许两个都访问相同的数据(忽略访问数据的线程安全)

  3. SomeDataDoesNotExist()的成本并不高

现在,这不一定局限于某些数据创建情况,但这是我可以想到的一个例子

我特别感兴趣的一部分是检查->;锁->;检查我曾在一些场合向开发人员解释过这种模式,他们一眼就看不到算法,但随后可能会欣赏它

无论如何,其他人也必须这样做。这是标准模式吗?它叫什么


共 (3) 个答案

  1. # 1 楼答案

    The part that I'm especially interested in identifying as a pattern is the check -> lock -> check.

    这叫做double-checked locking

    请注意,在较旧的Java版本(Java5之前)中,由于Java的内存模型是如何定义的,因此不安全。在Java5和更新版本中,对Java内存模型的规范进行了更改,使其现在是安全的

  2. # 2 楼答案

    使用双重检查锁定的延迟初始化

  3. # 3 楼答案

    虽然我可以看出你可能认为这看起来像是双重检查锁定,但实际上它看起来像是危险的破坏和不正确的双重检查锁定。如果没有SomeDataDoesNotExist和CreateSomeData的实际实现,我们无法保证这件事在每个处理器上都是线程安全的

    有关双重检查锁定如何出错的分析示例,请查看双重检查锁定的这个错误版本:

    C# manual lock/unlock

    我的建议是:如果没有令人信服的理由和内存模型专家的代码审查,不要使用任何低锁技术;你可能会弄错的。大多数人都是这样

    特别是,不要使用双重检查锁定,除非您能准确描述处理器可以代表您执行的内存访问重新排序,并提供一个令人信服的论据,证明您的解决方案在给定任何可能的内存访问重新排序时是正确的。当您稍微偏离已知的正确实现时,您需要从头开始分析。不能仅仅因为双重检查锁定的一个实现是正确的,就认为它们都是正确的;几乎没有一个是正确的