gpt4 book ai didi

java - Java Object Reference 发布不当

转载 作者:行者123 更新时间:2023-12-03 23:15:23 26 4
gpt4 key购买 nike

以下示例来自 Brian Goetz 所著的“Java 并发实践”一书,第 3 章,第 3.5.1 节。这是对象发布不当的示例:

class SomeClass {
public Holder holder;

public void initialize() {
holder = new Holder(42);
}
}

public class Holder {
private int n;
public Holder(int n) { this.n = n; }

public void assertSanity() {
if (n != n)
throw new AssertionError("This statement is false");
}
}

它表示 Holder 可能会以不一致的状态出现在另一个线程中,而另一个线程可能会观察到一个部分构造的对象。这怎么会发生?你能用上面的例子给出一个场景吗?

它还继续说,在某些情况下,线程可能会在第一次读取字段时看到一个陈旧的值,然后在下一次读取一个更新的值,这就是为什么 assertSanity 可以抛出 AssertionError。如何抛出 AssertionError

通过进一步阅读,解决此问题的一种方法是通过将变量 n 设置为 final 来使 Holder 不可变。现在,让我们假设 Holder 不是不可变的,但实际上是不可变的。

为了安全地发布这个对象,我们是否必须将 holder 初始化设置为静态并将其声明为 volatile (静态初始化和 volatile 或只是 volatile )?

像这样:

public class SomeClass {
public static volatile Holder holder = new Holder(42);
}

最佳答案

你可以想象一个对象的创建有很多非原子函数。首先你要初始化和发布 Holder。但是您还需要初始化所有私有(private)成员字段并发布它们。

好吧,JMM 没有规则让 holder 的成员字段的写入和发布发生在 holder 字段的写入之前,如发生在 初始化()。这意味着即使 holder 不为空,成员字段对其他线程不可见也是合法的。

你最终可能会看到类似的东西

public class Holder {
String someString = "foo";
int someInt = 10;
}

holder 不能为 null 但 someString 可以为 null 而 someInt 可以为 0。

据我所知,在 x86 架构下这是不可能发生的,但在其他架构下可能并非如此。

所以下一个问题可能是“为什么 volatile 会解决这个问题?”JMM 表示在 volatile 存储之前发生的所有写入对于 volatile 字段的所有后续线程都是可见的。

因此,如果 holder 是 volatile 并且您看到 holder 不为 null,则根据 volatile 规则,所有字段都将被初始化。

To safely publish this object, do we have to make holderinitialization static and declare it as volatile

是的,因为正如我提到的,如果 holder 变量不为空,那么所有写入都将可见。

How can the AssertionError be thrown?

如果一个线程注意到holder不为空,并且在进入方法并第一次读取n时调用AssertionError可能是0(默认值),n 的第二次读取现在可以看到来自第一个线程的写入。

关于java - Java Object Reference 发布不当,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28038122/

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