【发布时间】:2013-04-13 00:39:51
【问题描述】:
以下示例来自 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 可以抛出 @ 987654325@。 AssertionError怎么扔?
通过进一步阅读,解决此问题的一种方法是通过将变量 n 设为 final 来使 Holder 不可变。现在,让我们假设Holder 不是不可变的,但实际上是不可变的。
为了安全地发布此对象,我们是否必须将持有者初始化设为静态并将其声明为 volatile(静态初始化和 volatile 或只是 volatile)?
类似这样的:
public class SomeClass {
public static volatile Holder holder = new Holder(42);
}
【问题讨论】:
-
我所能看到的只是在多处理器情况下两个处理器之间缓存状态不一致的可能性。在非紧密耦合的 MP 环境中,这始终是可能的,除非您采取明确的同步步骤。
-
@PaulGrime - 经过简短的审查后,我没有看到任何可以解决上述情况的内容。在构造对象之前,引用不会“转义”。 int
n不公开,不能在课堂外查看。 -
不,但
holder是公共的,另一个线程可能会在其构造函数和字段初始化完成之前调用assertSanity- stackoverflow.com/a/4926812/319878。也许 - stackoverflow.com/a/10528614/319878. -
@PaulGrime - 在构造函数返回之前(如果 JITC 兼容),对创建对象的引用不会分配给
holder。 -
这些字段需要是 final 或 volatile 以保证它们在构造函数返回后完全初始化。
标签: java concurrency