在 Java Concurrency In Practice 中,给出了一个示例*来演示 visibility 的问题,其中两个不同的线程可能看不到由于缺乏同步,任何特定可变对象的最新状态。
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.");
}
}
在这个特定的例子中,该书指出,如果线程“A”首先初始化并通过线程不安全的方式发布它,例如:
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
然后线程“B”调用holder.assertSanity()
,由于状态不一致,完全有可能抛出AssertionError
。
现在,我理解了论证的基本前提,即对另一个线程永远不会观察到对可变变量的更改。但我在这里感到困惑的是它正在比较相同(或者我认为)的参考n != n
。
这不是比较可变原始字段private int n
的值吗?无论n
现在对于线程A的值是42,对于线程B的值是0(默认值),也不应该直接调用它来检查相同线程一致?即在线程A中调用assertSanity()
将检查42 != 42
是否在线程B中0 != 0
?
*参考3.5安全出版物,清单3.14& 3.15在书中。
答案 0 :(得分:1)
问题是在表达式n != n
中,变量n
将被加载两次(假设没有优化字节码)。在这两个负载之间,另一个线程可以改变该值。
答案 1 :(得分:1)
在线程B n != n
中进行比较时,B检索n
两次。同时,构造函数在线程A中运行会将n的值从默认值0修改为42。