下面的例子来自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");
}
}
Run Code Online (Sandbox Code Playgroud)
它表示Holder可能出现在另一个处于不一致状态的线程中,而另一个线程可能会观察到部分构造的对象.怎么会发生这种情况?你能用上面的例子给出一个场景吗?
此外,它继续说有一种情况,当一个线程第一次读取一个字段时可能会看到一个陈旧的值,然后下次再看到一个更新的值,这就是assertSanity可以抛出断言错误的原因.如何抛出assertionError?
从进一步阅读,解决这个问题的一种方法是通过使变量'n'最终使Holder不可变.现在,让我们假设持有人不是无法忍受的,而是有效的不可改变的.为了安全地发布这个对象,我们是否必须使holder初始化为静态并将其声明为volatile(静态初始化和volatile或者只是volatile)?就像是
public class someClass {
public static volatile Holder holder = new Holder(42);
}
Run Code Online (Sandbox Code Playgroud)
感谢您的帮助.
Joh*_*int 12
您可以想象一个对象的创建具有许多非原子功能.首先,您要初始化并发布Holder.但是您还需要初始化所有私有成员字段并发布它们.
那么JMM没有规则来编写和发布holder成员字段 - 在写入holder字段之前发生initialie().这意味着即使holder不为null,成员字段对其他线程还不可见是合法的.
你最终可能会看到类似的东西
public class Holder{
String someString = "foo";
int someInt = 10;
}
Run Code Online (Sandbox Code Playgroud)
holder可能不是null但someString可以为null,someInt可以为0.
根据我所知,在x86拱门下,这是不可能发生的,但在其他情况下可能并非如此.
所以下一个问题可能是Why does volatile fix this? JMM说在volatile列表之前发生的所有写操作都对volatile字段的所有后续线程都可见.
因此,如果holder是volatile并且您看到holder不为null,则基于volatile规则,将初始化所有字段.
要安全地发布此对象,我们是否必须将holder初始化设置为static并将其声明为volatile
是的,因为正如我所提到的,如果holder变量不为null,则所有写入都是可见的.
如何抛出assertionError?
如果一个线程注意到holder不是null,并且assertionError在进入该方法时调用并且n第一次读取可能是0(默认值),则第二次读取n现在可以看到来自第一个线程的写入.
| 归档时间: |
|
| 查看次数: |
1471 次 |
| 最近记录: |