Java同步 - 发布不正确

时间:2015-08-07 10:22:36

标签: java multithreading concurrency thread-safety

这是摘自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"。

  1. Java会将部分创建的对象放入" holder"构造函数退出前的引用(在构造函数初始化" n"字段之前)。 另一个线程将尝试调用" assertSanity"在这个部分创建的对象上。 因此" n!= n"操作必须足够长,才能发生AssertionError。

  2. 当本地缓存"持有者"当assertSanity()正在进行时,突然变得可见。

  3. 还有其他情况吗?

    感谢所有人!

1 个答案:

答案 0 :(得分:1)

由于重新排序,您无法真正考虑“此后会发生这种情况”。例如

  

Java将在构造函数退出之前(在构造函数初始化“n”字段之前)将部分创建的对象放入“holder”引用中

实际上可能会发生一个线程观察到构造函数已退出且对象已初始化,但另一个线程可能会看到引用(因此它也认为构造函数已退出),但对象的字段没有已为此主题初始化。

因此,事情变得非常难以预测,因为如果没有正确的同步,不同的线程可能会观察到不同顺序的状态变化或根本看不到。这里几乎不可能推断所有可能的情况:(

我强烈建议您阅读“Java Concurrency in Practice”一书中的“Java Memory Model”部分。

相关问题