【问题标题】:Java synchronization - Improper publicationJava 同步 - 不正确的发布
【发布时间】:2015-08-07 10:22:36
【问题描述】:

这是《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 将在构造函数退出之前(在构造函数初始化“n”字段之前)将部分创建的对象放入“持有人”引用中。 另一个线程将尝试在这个部分创建的对象上调用“assertSanity”。 因此,“n != n”操作必须足够长,才能发生 AssertionError。

  2. 当本地缓存的“持有人”在 assertSanity() 正在进行时突然变得可见时。

还有其他情况吗?

谢谢大家!

【问题讨论】:

    标签: java multithreading concurrency thread-safety


    【解决方案1】:

    由于重新排序,您无法真正考虑“这发生在之后”。例如

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

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

    所以,事情变得真的不可预测,因为如果没有适当的同步,不同的线程可能会以不同的顺序观察状态变化或根本看不到。这里几乎不可能推理出所有可能的情况:(

    我强烈建议阅读“Java 并发实践”一书中的“Java 内存模型”部分。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-20
      • 1970-01-01
      • 2010-11-28
      • 2013-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多