来自最终java
语义的field
文档:
他们保证会看到final
中设置的constructor
字段,请找到以下代码:
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
}
}
}
现在,我对volatile
和final
感到困惑。
据我了解,使用final
字段时请确保您无法在应用程序中更改变量,并且volatile
保证维持顺序并避免在关系发生之前发生。
所以我的问题是,它们如何使用final
变量而不是volatile
来保证可见性和排序?请帮忙。
答案 0 :(得分:1)
所以我的问题是,如何使用最终变量保证可见性和顺序
因为他们是用这种方式定义的,所以现在,遵循此定义是VM的工作。
当然,后续问题是:为什么他们用这种方式定义它?
这是由于Java内存模型的怪异“功能”所致。考虑这个例子
class Foo {
int x;
volatile int y;
Foo() {
x = 3;
y = 4;
}
static final Foo INSTANCE;
static {
INSTANCE= new Foo();
}
}
静态初始化器将被编译为以下代码(简化的伪代码):
Foo tmp = allocate(Foo.class)
Foo.INSTANCE = tmp
tmp.x = 3
tmp.y = 4
如您所见,该实例在执行构造函数之前已公开,volatile
在此不做任何更改。
对于大多数开发人员而言,此行为是意外的,并且可能导致非常难以调试的错误。因此,为了稍微减少此问题的范围,对规范进行了调整,以要求在实例公开之前初始化最终字段。
上面的示例,其中x
被声明为final
。
Foo tmp = allocate(Foo.class)
tmp.x = 3
Foo.INSTANCE = tmp
tmp.y = 4