这是摘自Book" Java Concurrency in Practice":
// Unsafe publication
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.");
}
}
...但更糟糕的是,其他线程可以看到最新的价值 持有人参考,但持有人的状态的陈旧价值。 ...
为了使事情更难以预测,线程可能会看到陈旧的价值 它第一次读取一个字段,然后是一个更新的值 下一次,这就是assertSanity可以抛出AssertionError的原因。 ...... 对象构造函数首先将默认值写入所有字段 在子类构造函数运行之前。因此可以看到 字段的默认值作为陈旧值。
我的问题:
似乎只有两种情况,其中assertSanity()可以抛出AssertionError - 当" Holder"实例是实例化过程和" n"的默认值。尚未设置" 42"。
Java会将部分创建的对象放入" holder"构造函数退出前的引用(在构造函数初始化" n"字段之前)。 另一个线程将尝试调用" assertSanity"在这个部分创建的对象上。 因此" n!= n"操作必须足够长,才能发生AssertionError。
当本地缓存"持有者"当assertSanity()正在进行时,突然变得可见。
还有其他情况吗?
感谢所有人!
答案 0 :(得分:1)
由于重新排序,您无法真正考虑“此后会发生这种情况”。例如
Java将在构造函数退出之前(在构造函数初始化“n”字段之前)将部分创建的对象放入“holder”引用中
实际上可能会发生一个线程观察到构造函数已退出且对象已初始化,但另一个线程可能会看到引用(因此它也认为构造函数已退出),但对象的字段没有已为此主题初始化。
因此,事情变得非常难以预测,因为如果没有正确的同步,不同的线程可能会观察到不同顺序的状态变化或根本看不到。这里几乎不可能推断所有可能的情况:(
我强烈建议您阅读“Java Concurrency in Practice”一书中的“Java Memory Model”部分。