安全发布可变对象

时间:2018-05-24 09:09:53

标签: java multithreading immutability

我读了几个相关问题,但没有一个解释安全发布持有人的方法。我仍然对Java Concurrency in Practice中的示例感到困惑,第3.5节:

有班主持人:

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种安全的出版习语,但我不明白,为什么它们会在上述情况下起作用:

  

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

     
      
  1. 从静态初始值设定项初始化对象引用;
  2.   
  3. 将对它的引用存储到volatile字段或AtomicReference中;
  4.   
  5. 将对它的引用存储到正确构造的对象的最终字段中;
  6.   
  7. 或将对它的引用存储到受到适当保护的字段中   一把锁。
  8.   

我同意1& 4,但怀疑为什么以下出版物会起作用:

//safe publication
public volatile Holder holder;

//safe publication
public final Holder holder;
挥发性& final只对引用有影响,而不是对引用的对象状态有影响,所以我认为AssertionError仍然可能,对吗?

作者没有对出版物进行改进,而是通过以下方式展示如何使Holder免受不安全出版物的影响:

private final int n;

我很好奇以下是否也有效?它与(有效)不变性有何关联?

private volatile int n;

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

2 个答案:

答案 0 :(得分:3)

实际上我认为volatile是最简单的解释。当操作重新排序时, Un 安全发布会发生,而volatile会阻止这种情况发生。我可能更有可能解释,但它是already explained far more accurate than I will do

正如here所解释的那样,基本上会插入适当的内存障碍来阻止重新排序。本质上,volatile的作用是,如果ThreadA读取由ThreadB执行的易失性更新,则保证还可以看到之前所做的所有更新,即易失性写入。

final也使事情变得安全,而且具体是written in the JLS

但根据以下情况有两种情况:将对它的引用存储到正确构造的对象的最终字段中

所以根据JLS,这是安全的出版物:

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

btw中插入了适当的内存屏障,可以防止构造函数中的存储重新排序,并发布引用本身。

这个例子怎么样?

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);
    }
 }

根据this

,这似乎仍然是安全的出版物

答案 1 :(得分:0)

使整数变为volatile&使用您同意的线程将其与锁定对象同步。

这不是确切的代码,但更多的是你的想法。没有两件东西可以一次操作一件事。这是什么导致程序甚至操作系统死锁。

的Class1:

public static final Object lock = new Object();
private Holder holder;
public abstract void method1(); //Assume these two go to different places
public abstract void method2(); //At different times w/ different implementations

线程1:

public void method1() {
    synchronized(Class1.lock) {
        holder.assertMadness();
    }
}

线程2:

public void method2() {
    synchronized(Class1.lock) {
        holder.assertMadness();
    }
}