6

在并发情况下创建类字段是否可以volatile防止所有内存可见性问题?是否有可能对于下面的类,获取Test对象引用的线程首先看到x0(的默认值int)然后看到 10?我认为这是可能的,当且仅当构造函数在没有完成(不正确的发布)的情况下Test放弃参考。this有人可以验证/纠正我吗?

class Test {
    volatile int x = 10;            
}

第二个问题:如果是final int x=10;呢?

4

2 回答 2

6

根据 JMM,您实际上并不能保证看到 x = 10。

例如,如果您有

Test test =  null;

Thread 1 -> test = new Test();
Thread 2 -> test.x  == // even though test != null, x can be seen as 0 if the
                       // write of x hasn't yet occur

现在如果你有

class Test{
  int y = 3;
  volatile x = 10;
}

如果线程 2 读取 x == 10,线程 2 保证读取 y == 3

回答你的第二个问题。

拥有 final 字段将在构造函数之后和发布之前发出 storestore,因此拥有 final 字段实际上将确保您看到 x = 10。

编辑:正如 yshavit 所说。你失去了我在我的第一个示例中提到的最终字段的发生之前的关系,也就是说,如果线程 2 读取 x == 10,它可能不会读取 y == 3,其中 x 是最终字段。

于 2012-01-06T22:06:33.860 回答
3

即使在单线程实现中,如果在构造函数中泄漏this ,也不能保证看到 x = 10。因此,您在这里遇到的问题不是直接的并发问题,而是执行顺序问题(取决于您何时泄漏this)。例如,如果你在实例的父构造函数中泄漏这个:

public class TestParent
{
  public TestParent()
  {
    if (this instanceof TestChild)
    {
      TestChild child = (TestChild) this;
      System.out.println(child.field);  // will print 0 when TestChild is instantiated.
    }
  }
}


public class TestChild extends TestParent
{
  volatile int field = 10;
}

public static void main(String[] args)
{
  TestChild child = new TestChild();
  System.out.println(child.field);

  // The above results in 0 (from TestParent constructor) then 10 being printed.
}

另一方面,最终字段保证具有分配的初始值,只要该分配是在声明行上完成的(如果您将字段设为最终但在构造函数中对其进行了初始化,那么您仍然可以在之前泄漏并显示未初始化的值。

于 2012-01-06T22:17:05.790 回答