gpt4 book ai didi

java - Java Object Reference 发布不当

转载 作者:IT老高 更新时间:2023-10-28 20:54:18 25 4
gpt4 key购买 nike

以下示例来自 Brian Goetz 的《Java Concurrency in Practice》一书,第 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 可以抛出 AssertionErrorAssertionError 怎么抛出?

进一步阅读,解决此问题的一种方法是通过将变量 n 设为 final 来使 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 不为空,根据 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不为null,并且在进入方法并第一次读取n时调用AssertionError可能是0(默认值),n 的第二次读取现在可能会看到来自第一个线程的写入。

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

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