Java 最终字段Vs.Volatile
从finalJava 最终字段Vs.Volatile,java,final,Java,Final,从final字段的java文档中语义: 他们保证看到构造函数中设置的最终字段,请查找以下代码: class FinalFieldExample { final int x; int y; static FinalFieldExample f; public FinalFieldExample() { x = 3; y = 4; } static void writer() { f =
字段的java
文档中
语义:
他们保证看到构造函数中设置的最终
字段,请查找以下代码:
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
来保证可见性和顺序?请帮忙
所以我的问题是,他们如何保证使用最终变量的可见性和顺序
因为他们是这样定义的,现在VM的工作就是遵守这个定义
当然,接下来的问题是:他们为什么这样定义它
这是因为Java内存模型有一个奇怪的“特性”。考虑这个例子
class Foo {
int x;
volatile int y;
Foo() {
x = 3;
y = 4;
}
static final Foo INSTANCE;
static {
INSTANCE= new Foo();
}
}
静态初始化器将编译成以下代码(简化伪代码):
如您所见,在执行构造函数之前,实例是公开的,volatile
在这里不会改变任何东西
这种行为对于大多数开发人员来说是意外的,并且可能导致非常难以调试的bug。因此,为了稍微减少这个问题的扩展,对规范进行了调整,要求在公开实例之前初始化最终字段
上面的示例,声明了x
final
Foo tmp = allocate(Foo.class)
tmp.x = 3
Foo.INSTANCE = tmp
tmp.y = 4
volatile
主要是关于可见性的。我不明白你的问题。。您能重新措辞吗?完成,请现在检查。final
不保证订购。它确实保证了可见性,因为当一个字段是final
时,它将不再被更改。如果一个线程缓存了该值,则该值对所有线程都保持不变。在可见性方面:每个线程都看到相同的东西。那么这是否意味着最终变量将不属于CPU缓存的一部分,而是直接写入内存中?从而确保不同CPU内核的所有线程始终看到最新的值。否,final
与缓存本身无关。它保证了可见性,因为当字段初始化时,它将不再更改。这就是final
的意思。最初使用该字段的每个线程都不会看到不同的值。在逻辑上,它总是一样的。线程可以在CPU寄存器或本地缓存中缓存该值,这不会对程序产生负面影响,因为该值总是相同的。不需要同步。值的可见性始终是有保证的。您是如何得出这个结论的?静态初始化程序将被编译成这个吗?@Savior,请参阅“显示它不工作的测试用例”,这是一篇非常过时的文章。在任何情况下,如果您问题中的示例反映了OP中的示例,我可能会同意。但您是在执行自身同步的静态
块中进行实例化的。在初始化完成之前,没有其他线程可以看到实例。如果你知道更多,请分享。我不应该说过时。我只是指过时的和非正式的。你能引用一些更正式的东西(比如JLS)并回答我之前的评论吗。对于您开始使用的示例代码,您关于事物顺序的声明是错误的。
Foo tmp = allocate(Foo.class)
tmp.x = 3
Foo.INSTANCE = tmp
tmp.y = 4