这个图案的名字?(答:带双重检查锁定的延迟初始化)
考虑下面的代码:
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();
}
}
首先,我需要说明一些假设:
有一个很好的理由,我不能在应用程序启动后就这么做。可能数据还不可用,等等。
Foo可以从两个或多个线程实例化并并发使用。我希望其中一个最终创建一些数据(但不是两个),然后我将允许两个都访问相同的数据(忽略访问数据的线程安全)
SomeDataDoesNotExist()的成本并不高
现在,这不一定局限于某些数据创建情况,但这是我可以想到的一个例子
我特别感兴趣的一部分是检查->;锁->;检查我曾在一些场合向开发人员解释过这种模式,他们一眼就看不到算法,但随后可能会欣赏它
无论如何,其他人也必须这样做。这是标准模式吗?它叫什么
# 1 楼答案
这叫做double-checked locking
请注意,在较旧的Java版本(Java5之前)中,由于Java的内存模型是如何定义的,因此不安全。在Java5和更新版本中,对Java内存模型的规范进行了更改,使其现在是安全的
# 2 楼答案
使用双重检查锁定的延迟初始化
# 3 楼答案
虽然我可以看出你可能认为这看起来像是双重检查锁定,但实际上它看起来像是危险的破坏和不正确的双重检查锁定。如果没有SomeDataDoesNotExist和CreateSomeData的实际实现,我们无法保证这件事在每个处理器上都是线程安全的
有关双重检查锁定如何出错的分析示例,请查看双重检查锁定的这个错误版本:
C# manual lock/unlock
我的建议是:如果没有令人信服的理由和内存模型专家的代码审查,不要使用任何低锁技术;你可能会弄错的。大多数人都是这样
特别是,不要使用双重检查锁定,除非您能准确描述处理器可以代表您执行的内存访问重新排序,并提供一个令人信服的论据,证明您的解决方案在给定任何可能的内存访问重新排序时是正确的。当您稍微偏离已知的正确实现时,您需要从头开始分析。不能仅仅因为双重检查锁定的一个实现是正确的,就认为它们都是正确的;几乎没有一个是正确的