英文:
Final Field Semantics
问题
17.5. final Field Semantics
示例 17.5-1. Java 内存模型中的 final 字段
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; // 保证看到的是 3
int j = f.y; // 可能看到 0
}
}
}
我已经被这个问题困扰了好几天。
是否有人可以直接回答我为什么 f.y 可能会看到 0?
英文:
17.5. final Field Semantics
Example 17.5-1. final Fields In The Java Memory Model
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
}
}
}
I have been troubled by this problem for several days.
Could anyone directly answer me why it the f.y could be see 0?
答案1
得分: 2
如果线程A赋值y
:
线程A:
writer();
而线程B读取y
:
线程B:
reader();
那么y
在没有任何同步的情况下被读取,因此可能无法看到赋值的值。这是Java同步要求的一个简单应用。如果您仍然不理解,请在您的问题中进行澄清。
换句话说,如果将y
声明为volatile
,那么将保证能够看到它。
class FinalFieldExample {
final int x;
volatile int y;
然后:
static void reader() {
if (f != null) {
int i = f.x; // 保证能看到3
int j = f.y; // 保证能看到4
英文:
If thread A assigns y
:
Thread A:
writer();
And thread B reads y
:
Thread B
reader();
Then y
has been read without any synchronization and therefore might not see the value assigned. It's a simple application of Java's synchronization requirement. If you still don't understand it please clarify in your question.
To put this another way, if y
were declared as volatile
then it would be guaranteed to be seen.
class FinalFieldExample {
final int x;
volatile int y;
Then:
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // guaranteed to see 4
答案2
得分: 1
这是因为在写操作[this.]y=4
和读操作f.y
之间缺乏happens-before关系。赋值y=4
在构造函数完成之前发生,构造函数在对静态字段赋值之前完成,但由于两个线程直接访问静态字段,且它们之间没有直接的顺序关系,因此没有_正式保证_非final字段y
的写入在读取线程中是可见的。Java语言规范(JLS)确实对final字段做出了_特定_的承诺;实质上,存在一条未明确表达的_happens-before_规则(可能本应如此)。
任何建立_happens-before_关系的机制都将解决这个理论问题,包括将y
或f
之中的任意一个声明为volatile
。
(需要注意的是,我非常怀疑曾经存在过任何一种实现,会重新排序使得对f
的赋值在构造函数完成之前执行,但JLS在指出除了对final字段外,并没有_保证_不会发生这种情况。)
英文:
This is because there is no happens-before relationship between the write operation [this.]y=4
and the read operation f.y
. The assignment y=4
happens-before the constructor finishes, and the constructor finishes before the assignment to the static field, but since both threads are directly accessing a static field and do not have a direct joint sequencing relationship, there is no formal guarantee that the write to the non-final field y
is visible in the reading thread. The JLS does make a specific promise about final fields; in essence, there is a happens-before rule that isn't being expressed explicitly (and probably should).
Any mechanism for creating a happens-before relationship would solve this theoretical problem, including making either y
or f
volatile
.
(Note that I very seriously doubt that any implementation has ever existed that would resequence such that the assignment to f
was executed before the constructor finished, but the JLS is making the point that there is no guarantee except in the case of final fields that this won't happen.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论