4

在针对双重检查锁定场景的乱序写入提到的示例中(参考: IBM 文章Wikipedia 文章

我无法理解为什么在构造函数完全初始化之前 Thread1 会出现同步块的简单原因。根据我的理解,创建“new”和调用构造函数应该按顺序执行,并且在所有工作未完成之前不应释放同步锁。

请让我知道我在这里缺少什么。

4

4 回答 4

12

构造函数可能已经完成——但这并不意味着该构造函数中涉及的所有写入都对其他线程可见。糟糕的情况是,在对象的内容变得可见之前,引用对其他线程可见(因此它们开始使用它)。

您可能会发现Bill Pugh 关于它的文章也有助于阐明一点。

就我个人而言,我只是避免像瘟疫一样进行双重检查锁定,而不是试图让它全部工作。

于 2012-06-25T18:54:27.650 回答
1

当线程 1 位于 //3 时,线程 2 检查实例是否为空。

public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) {  //1
  if (instance == null)          //2
    instance = new Singleton();  //3
  }
}
 return instance;//4
}

此时,实例内存已从堆中分配,指向它的指针存储在实例引用中,因此线程 2 执行的“if 语句”返回“false”。请注意,由于在 Thread2 检查时 instance 不为 null,因此线程 2 不会进入同步块,而是返回对“完全构造但部分初始化的 Singleton 对象”的引用。

于 2012-06-25T19:18:23.987 回答
1

有问题的代码在这里:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {  //1
      if (instance == null)          //2
        instance = new Singleton();  //3
    }
  }
  return instance;
}

现在,只要您一直认为代码按照编写的顺序执行,就无法理解这个问题。即便如此,在当今主流的对称多处理架构中也存在跨多个处理器(或内核)的缓存同步问题。

例如,Thread1 可以发布对instance主内存的引用,但无法发布Singleton创建的对象内的任何其他数据。Thread2 将观察对象处于不一致的状态。

只要 Thread2 不进入synchronized块,缓存同步就不必发生,因此 Thread2 可以无限期地继续下去,而无需观察到Singleton一致的状态。

于 2012-06-25T19:31:47.743 回答
0

代码没有按照编写的顺序执行,这是一个普遍的问题。在 Java 中,线程只需要与自身保持一致。在一条instance线上创建的new必须准备好继续下一条。其他线程没有这样的义务。例如,如果fieldA是 1 并且 'fieldB' 是 2 进入线程 1 上的代码:

fieldA = 5;
fieldB = 10;

和线程 2 运行此代码:

int x = fieldA;
int y = FieldB;

xy 值 1 2、5 2 和 5 10 都是意料之中的,但 1 10——fieldB 在 fieldA 之前被设置和/或拾取——是完全合法的,而且很可能也是如此。所以双重检查锁定是一个更普遍问题的特例,如果您使用多个线程,您需要注意它,特别是如果它们都访问相同的字段。

应该提到 Java 1.5 中的一个简单解决方案:标记volatile的字段保证在被引用之前立即从主内存中读取并在之后立即写入。如果声明fieldAfieldB以上volatile,则 xy 值不可能为 1 10。如果instance是 volatile,则双重检查锁定有效。使用volatile字段是有成本的,但它比同步要少,因此双重检查锁定成为一个不错的主意。这是一个更好的主意,因为它避免了在 CPU 内核空闲时有一堆线程等待同步。

But you do want to understand this (if you can't be talked out of multithreading). On the one hand you need to avoid timing problems and on the other avoid bringing your program to a halt with all the threads waiting to get into synch blocks. And it's very difficult to understand.

于 2012-06-26T19:45:25.353 回答