当前位置: 首页 > 知识库问答 >
问题:

可变对象的安全发布

申高峰
2023-03-14
public Holder {
    private int n;
    public Holder(int n) { this.n = n };
    public void assertSanity() {
        if(n != n)
             throw new AssertionError("This statement is false.");
    }
}

及其不安全的发布:

//unsafe publication
public Holder holder;
    public void initialize() {
        holder = new Holder(42);
    }

可以抛出AssertionError,我同意。作者写道,这是因为不安全的出版,但另一方面没有答案:什么才是正确的出版方式?它们表示了4个安全发布习惯用语,但我不明白,为什么它们会在上面的情况下起作用:

要安全地发布对象,必须同时使对对象的引用和对象的状态对其他线程可见。通过以下方法可以安全地发布构造正确的对象:

    null
//safe publication
public volatile Holder holder;
//safe publication
public final Holder holder;
private final int n;
private volatile int n;

这里是我的第一个问题,谢谢你的帮助!

共有1个答案

阴永逸
2023-03-14

实际上,我认为volatile在这里是最简单的解释。当操作可以重新排序时,就会发生不安全的发布,而volatile可以防止这种情况。我可以解释得更可能,但它已经解释得比我要准确得多了。

基本上,下面将插入适当的内存屏障,以防止重新排序,如这里所解释的。从本质上讲,volatile所做的是,如果一个线程a读取了一个由ThreadB执行的volatile更新,那么它保证也会看到在volatile写入之前完成的所有更新。

final也使事情变得安全,它是专门在JLS中编写的。

但是这里有两种情况:将对它的引用存储到正确构造的对象的最后一个字段中。

因此,根据JLS,这是安全发布:

class Holder {
    private final int n; // making final here
}

有适当的内存障碍插入,以防止在发布引用本身时对构造函数中的存储进行重新排序。

static class Holder {

   private int n;

   public void setN(int n){
      this.n = n;
   }
}
 class Other {
    final Holder holder;
    Other(){
        holder = new Holder();
        holder.setN(12);
    }
 }
 类似资料:
  • 从静态初始值设定项初始化对象引用 将对它的引用存储到volatile字段或atomicreference 将对它的引用存储到正确构造的对象的最后一个字段 将对它的引用存储到由锁正确保护的字段中。 但是,我对第二个成语感到困惑。因为只能保证引用对另一个线程是可见的,但它没有它所引用的对象构造的同步。那么它如何保证可变对象是正确构造的,构造这个对象的线程是什么,被另一个线程打断了呢?

  • 类OneValueCache是不可变的。但是我们可以更改变量缓存的引用。 但我不能理解为什么VolateCachedFactorizer类是线程安全的。 对于两个线程(线程A和线程B),如果线程A和线程B同时到达,那么两个线程A和B都将尝试创建OnEvalueCache。然后线程A到达而线程B同时到达。然后线程A将创建一个,它覆盖threadB创建的值(OneValueChange是不可变的,但是

  • 现在,我在https://www.cs.umd.edu/~pugh/java/memorymodel/jsr-133-faq.html中读到,“现在,在说了所有这些之后,如果在一个线程构造了一个不可变的对象(即一个只包含final字段的对象)之后,您希望确保其他所有线程都能正确地看到它,那么您通常仍然需要使用同步。没有其他方法可以确保,例如,对不可变对象的引用会被第二个线程看到。程序从最终字段得到

  • 我得到的是它们是线程安全的。 杰里米·曼森博客的片段- 因为this引用存储在lastconstructed中“,因此转义构造函数 请建议。

  • 问题内容: 阅读“实践中的Java并发性”,第3.5节包含以下内容: 除了创建两个的明显的线程安全隐患外,该书还声称可能会发生发布问题。 此外,对于诸如 一个可以扔! 这怎么可能?我能想到的唯一允许这种荒谬行为的方法是,如果构造函数不被阻塞,那么当构造函数代码仍在另一个线程中运行时,将创建对实例的引用。 这可能吗? 问题答案: 之所以可行,是因为Java的内存模型较弱。它不保证读写顺序。 可以通过

  • 问题内容: 我正在努力使可变对象与不可变对象有关。使用可变对象会带来很多负面影响(例如,从方法中返回字符串数组),但是我很难理解它的负面影响。使用可变对象的最佳实践是什么?您是否应尽可能避免使用它们? 问题答案: 好吧,这有几个方面。 没有参考身份的可变对象会在奇数时间导致错误。例如,考虑使用基于值的方法的 : 当实例用作键时,实例在映射中“丢失”,因为实例和相等性基于可变值。这些值在映射之外更改