使字段“易失性”是否可以在并发情况下防止所有内存可见性问题



使类字段volatile在并发情况下是否可以防止所有内存可见性问题?对于下面的类,获得Test对象引用的线程是否可能首先将x视为0(int的默认值),然后将其视为10?我认为这是可能的当且仅当Test的构造函数在未完成的情况下泄露this引用(不正确发布)。有人能验证/纠正我吗?

class Test {
    volatile int x = 10;            
}

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

根据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所指出的。你失去了我在第一个关于final字段的例子中提到的先发生后发生的关系,也就是说,正如yshavit所说,如果线程2读取x==10,它可能不会读取y==3,其中x是final字段。

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

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.
}

另一方面,只要在声明行上完成赋值,就保证最终字段具有指定的初始值(如果您使字段成为最终字段,但在构造函数中初始化它,则您仍然可以在之前泄漏this并显示未初始化的值。

相关内容

最新更新