在并发情况下创建类字段是否可以volatile
防止所有内存可见性问题?是否有可能对于下面的类,获取Test
对象引用的线程首先看到x
0(的默认值int
)然后看到 10?我认为这是可能的,当且仅当构造函数在没有完成(不正确的发布)的情况下Test
放弃参考。this
有人可以验证/纠正我吗?
class Test {
volatile int x = 10;
}
第二个问题:如果是final int x=10;
呢?
在并发情况下创建类字段是否可以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 所说。你失去了我在我的第一个示例中提到的最终字段的发生之前的关系,也就是说,如果线程 2 读取 x == 10,它可能不会读取 y == 3,其中 x 是最终字段。
即使在单线程实现中,如果在构造函数中泄漏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.
}
另一方面,最终字段保证具有分配的初始值,只要该分配是在声明行上完成的(如果您将字段设为最终但在构造函数中对其进行了初始化,那么您仍然可以在之前泄漏它并显示未初始化的值。