在乱序中提到的例子写的双重检查锁定的情况(参考: IBM的文章和维基百科文章 )
我不明白为什么线程1会出面了synchronized块构造完全初始化前的原因很简单。 按我的理解,创造“新”和调用构造函数应该按顺序执行和同步锁不应该被释放,直到所有的工作没有完成。
请让我知道我在这里失踪。
在乱序中提到的例子写的双重检查锁定的情况(参考: IBM的文章和维基百科文章 )
我不明白为什么线程1会出面了synchronized块构造完全初始化前的原因很简单。 按我的理解,创造“新”和调用构造函数应该按顺序执行和同步锁不应该被释放,直到所有的工作没有完成。
请让我知道我在这里失踪。
构造函数可以完成 - 但是,这并不意味着所有的构造函数中所涉及的已经被写入可见于其他线程。 讨厌的情况是,当基准成为其他线程可见的(所以他们开始使用它)成为可见的对象的内容之前。
您可能会发现比尔Pugh的文章就可以帮助流下了小光,太多。
个人而言,我只是避免双重检查锁定瘟疫一样,而不是试图使一切工作。
有问题的代码是在这里:
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
}
现在,这个问题不能,只要你一直以为的代码编写程序的顺序执行的理解。 即使是这样,有一个对称多架构,这是今天的主流跨多个处理器(或核)高速缓存同步的问题。
线程1可以例如发布instance
参考主存储器,但未能发布内部的任何其他数据Singleton
所创建对象。 线程2将处于不一致的状态观察的对象。
只要线程2不会进入synchronized
块,缓存同步没有发生,所以线程2可以无限期地继续下去而没有观察Singleton
一致的状态。
线程2个检查是否实例为null时线程1处于// 3。
public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;//4
}
在这一点上,例如内存已经从堆中分配和指针,它存储在实例的引用,因此,“if语句”由线程执行2返回“假”。 需要注意的是,因为实例不为空时线程2检查它,线程2不进入同步块,而是返回到基准“完全构造,但部分初始化,Singleton对象”。
有一个与代码在它的书写顺序没有被执行的一个普遍问题。 在Java中,线程仅有责任与自身相符。 一个instance
与在同一行中创建new
必须是准备去下。 有没有这样的oblgation给其他线程。 例如,如果fieldA
是1和“fieldB”是2进入这个代码在线程1:
fieldA = 5;
fieldB = 10;
和线程2个运行这段代码:
int x = fieldA;
int y = FieldB;
的1 2 XY值,5 2和5 10都是可以预期的,但1 10 - fieldB设定和/或前FIELDA拾取 - 是完全合法的,以及有可能的,也是如此。 所以,双重检查锁定是一个更一般的问题的一个特例,如果你用多线程工作,你需要意识到这一点,尤其是当它们都访问相同的字段。
从Java 1.5的一个简单的解决方案应该提到:标volatile
,保证可以从主内存中读取立即被引用后,立即写了。 如果fieldA
和fieldB
上述被宣布volatile
,1 10的xy值将是不可能的。 如果instance
是挥发性的,双重检查锁定的工作方式。 还有使用成本volatile
领域,但它的不足同步,所以双重检查锁定成为一个不错的主意。 这是一个更好的主意,因为它避免了一堆等着,而CPU核心都处于闲置状态,以同步线程。
但是,你要明白这一点(如果你不能谈出多线程)。 一方面,你需要避免时序问题,而在另一避免把你的程序停止与所有的线程正在等待进入同步块。 而且这很难理解。