Java同步-发布不正确

Java同步-发布不正确,java,multithreading,concurrency,thread-safety,Java,Multithreading,Concurrency,Thread Safety,这是《实践中的Java并发》一书的摘录: 。。。由于可见性问题,支架可能会出现在其他人面前 线程处于不一致的状态,即使其不变量是 由其构造函数正确建立!这是一篇不恰当的文章 可能允许另一个线程观察部分构造的对象。 。。。但更糟糕的是,其他线程可能会看到 持有者引用,但持有者状态的过期值 为了使事情更加不可预测,线程可能会看到过时的值 它第一次读取字段,然后读取更为最新的值 下一次,这就是为什么assertSanity会抛出AssertOnError。。。这个 对象构造函数首先将默认值写入所有字

这是《实践中的Java并发》一书的摘录:

。。。由于可见性问题,支架可能会出现在其他人面前 线程处于不一致的状态,即使其不变量是 由其构造函数正确建立!这是一篇不恰当的文章 可能允许另一个线程观察部分构造的对象。

。。。但更糟糕的是,其他线程可能会看到 持有者引用,但持有者状态的过期值

为了使事情更加不可预测,线程可能会看到过时的值 它第一次读取字段,然后读取更为最新的值 下一次,这就是为什么assertSanity会抛出AssertOnError。。。这个 对象构造函数首先将默认值写入所有字段 在子类构造函数运行之前。因此,可以看到 字段的默认值为过时值

我的问题:

似乎只有两种情况下assertSanity()可以抛出AssertOnError—当“Holder”实例处于实例化过程中且“n”的默认值尚未设置为“42”时

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

  • 当assertSanity()正在进行时,本地缓存的“holder”突然变为可见

  • 还有其他情况吗


    谢谢大家

    由于重新排序,你不能真正用“这件事发生在那之后”来思考。比如说

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

    实际上,可能会发生这样的情况:一个线程观察到构造函数已退出,对象已初始化,但另一个线程可能看到引用(因此它也认为构造函数已退出),但对象的字段尚未为此线程初始化

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

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

    // 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.");
        }
    }