gpt4 book ai didi

java - 初始化非最终字段

转载 作者:搜寻专家 更新时间:2023-10-30 19:45:02 25 4
gpt4 key购买 nike

我目前正在阅读 JSR-133(Java 内存模型),我不明白为什么 f.y 可能未初始化(可以看到 0)。谁能给我解释一下?

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
}
}
}

最佳答案

这称为“过早发布”效应。

简单来说,如果这种重新排序不违反 JMM 的限制,则允许 JVM 重新排序程序指令(出于性能原因)。

您希望代码 f = new FinalFieldExample(); 像这样运行:

1.创建 FinalFieldExample
的实例2. 将3赋值给x
3. 将 4 赋值给 y
4. 将创建的对象赋值给变量f

但在提供的代码中,没有什么可以阻止 JVM 重新排序指令,因此它可以运行如下代码:

1.创建 FinalFieldExample
的实例2. 将3赋值给x
3. 将未完全初始化的原始对象分配给变量 f
4. 将 4 赋值给 y

如果重新排序发生在单线程环境中,我们甚至不会注意到它。那是因为我们期望,对象将在我们开始使用它们之前被完全创建,并且 JVM 尊重我们的期望。现在,如果多个线程同时运行这段代码会发生什么?在下一个示例中,Thread1 正在执行方法 writer() 和 Thread2 - 方法 reader():

线程 1:创建 FinalFieldExample
的实例线程 1:将 3 赋值给 x
线程 1:将未完全初始化的原始对象分配给变量 f
线程2:读取f,不为空
线程2:读f.x,是3
线程2:读取f.y,还是0
线程 1:将 4 赋值给 y

绝对不好。为了防止 JVM 这样做,我们需要给它关于程序的额外信息。对于这个特定的例子,有一些方法可以修复内存一致性:

  • y 声明为final 变量。这将导致“freeze”效果。简而言之,final 变量将始终在您访问它们的那一刻被初始化,如果在构造期间没有泄漏对对象的引用
  • f 声明为volatile 变量。这将创建“synchronization order”并解决问题。简而言之,指令不能在 volatile 写入之下和 volatile 读取之上重新排序。分配给 f 变量是 volatile 写入,这意味着 new FinalFieldExample() 指令不能在分配后重新排序和执行。从f变量读取是volatile读取,所以读取f.x不能在它之前执行。 v-write 和 v-read 的组合称为同步顺序,可提供所需的内存一致性。

Here是一个很好的博客,可以回答您关于 JMM 的所有问题。

关于java - 初始化非最终字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31223219/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com