gpt4 book ai didi

java - 是否可以对实例初始化和分配给共享变量进行重新排序?

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

我正在阅读 an essay ,它实际上谈论的是双重检查锁定,但令我惊讶的是,作为示例提供的代码中出现了更基本的故障。那里指出,实例的初始化(即在构造函数返回之前写入实例变量)可能会被重新排序到 after 对实例的引用被写入共享变量(以下示例中的静态字段)。

对于 Foo 类的以下定义,是否有一个线程执行 Foo.initFoo(); 而另一个线程执行 System.out .println(Foo.foo.a);,第二个线程可能会打印 0(而不是 1 或抛出 NullPointerException )?

class Foo {
public int a = 1;

public static Foo foo;

public static void initFoo() {
foo = new Foo();
}

public static void thread1() {
initFoo(); // Executed on one thread.
}

public static void thread2() {
System.out.println(foo.a); // Executed on a different thread
}
}

根据我对 Java 内存模型(以及其他语言的内存模型)的了解,这实际上并不让我感到惊讶,但直觉强烈认为这是不可能的(可能是因为涉及到对象初始化和对象初始化在 Java 中显得如此神圣)。

是否可以在第一个线程中不同步的情况下“修复”此代码(即它永远不会打印 0)?

最佳答案

foo = new Foo(); 的调用涉及多个操作,这些操作可能会重新排序,除非您引入适当的同步来阻止它:

  1. 为新对象分配内存
  2. 写入字段的默认值(a = 0)
  3. 写入字段的初始值(a = 1)
  4. 发布对新创建对象的引用

如果没有适当的同步,第 3 步和第 4 步可能会重新排序(请注意,第 2 步必然发生在第 4 步之前),尽管 x86 架构上的热点不太可能发生。

为了防止它,您有多种解决方案,例如:

  • 使成为最终
  • 同步访问 foo(使用同步的 init 和 getter)。

无需深入了解 JLS #17 的复杂性,您可以阅读 JLS #12.4.1关于类初始化(强调我的):

The fact that initialization code is unrestricted allows examples to be constructed where the value of a class variable can be observed when it still has its initial default value, before its initializing expression is evaluated, but such examples are rare in practice. (Such examples can be also constructed for instance variable initialization.) The full power of the Java programming language is available in these initializers; programmers must exercise some care. This power places an extra burden on code generators, but this burden would arise in any case because the Java programming language is concurrent.

关于java - 是否可以对实例初始化和分配给共享变量进行重新排序?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16011126/

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