使类字段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并显示未初始化的值。