关于在对象的构造函数完成之前对对象的引用
你们每个人都知道JMM的这个功能,有时在这个对象的构造函数完成之前,对对象的引用可以接收值。
在 JLS7 第 17.5 页的最后一个字段语义中,我们也可以读到:
字段的使用模型很简单:在对象的构造函数中设置对象的字段;并且不要在对象的构造函数完成之前,在另一个线程可以看到它的位置写入对正在构造的对象的引用。如果遵循此命令,则当另一个线程看到该对象时,该线程将始终看到该对象字段的正确构造版本。
final
final
final
(1)
在JLS之后,示例如下,它演示了如何不保证初始化非最终字段(1示例17.5-1.1):(2)
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}
此外,在这个问答中,格雷先生写道:
如果将字段标记为,则构造函数保证作为构造函数的一部分完成初始化。否则,在使用锁之前,您必须在锁上进行同步。
final
(3)
所以,问题是:
1)根据语句(1),我们应该避免在其构造函数完成之前共享对不可变对象的引用
2)根据JLS给出的示例(2)和结论(3),似乎我们可以在其构造函数完成之前安全地共享对不可变对象的引用,即当其所有字段都为。final
是不是有些矛盾?
编辑1:我到底是什么意思。如果我们以这种方式修改类,则该字段也将是(2):y
final
class FinalFieldExample {
final int x;
final int y;
...
因此,在方法上可以保证:reader()
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4, isn't it???
如果是这样,为什么我们应该避免在它的构造函数完成之前(根据(1))编写对对象的引用,当所有字段都是最终的?f
f