可见性保证

Cap*_*ngs 6 java concurrency multithreading memory-visibility

我已经阅读了第16.3节"初始化安全性"的一些解释 JCIP但仍然不清楚.该部分指出

"此外,任何可以通过正确构造的对象的最终字段(例如最终数组的元素或最终字段引用的HashMap的内容)到达的变量也可以保证对其他线程可见."

所以,如果我有以下可变对象:

public final class Container{
    private String name;
    private int cupsWon;
    private double netWorth;

        public Container( String name, int cupsWon, double netWorth ){
             this.name = name;
             this.cupsWon = cupsWon;
             this.netWorth = netWorth;
        }

    //NO Setters
    //Getters
}
Run Code Online (Sandbox Code Playgroud)

然后,线程1按如下方式创建它并将c传递给Thread2.

final Container c = new Container("Ted Dibiasi", 10, 1000000);
Run Code Online (Sandbox Code Playgroud)

Thread2(不是同时,假设在1 ms之后),读取c的值,是否有可能看到Thread2

c.name=null or
c.cupswon=0 or worst of all, 
c.netWorth=0.0?
Run Code Online (Sandbox Code Playgroud)

干杯

UPDATE

我注意到有关吸气鬼的课程有些困惑.我正在更新源代码,希望这将是明确的.谢谢大家一起来看看.

public final class Container{

    private String name;
    private int cupsWon;
    private double netWorth;

    public Container( String name, int cupsWon, double netWorth ){
        this.name = name;
        this.cupsWon = cupsWon;
        this.netWorth = netWorth;
    }

    public final String getName(){
        return name;
    }

    public final int getCupsWon(){
        return cupsWon;
    }

    public final double getNetWorth(){
        return netWorth;
    }

}
Run Code Online (Sandbox Code Playgroud)

// ----------

public final class Producer{

    private final Client client;

    public Producer( Client client ){
         this.client = client;
    }

    //Thread1 call produce()   
    public final void produce( ){
        final Container c = new Container("Ted Dibiasi", 10, 1000000);
        client.update( c );
    }

}
Run Code Online (Sandbox Code Playgroud)

// ----

public final class Client{

     private Container c;
     //private volatile Container c;       

     public final void update( Container c ){
          this.c = c;
     }

     //Thread2 calls consume().
     public final void consume( ){
          String name = c.getName();
          int cupsWon = c.getCupsWon();
          double netWorth = c.getNetWorth();           
     }

 }
Run Code Online (Sandbox Code Playgroud)

我的问题是:

a)当Thread2调用consume()时,name,cupsWon,netWorth可以为null,0还是0.0?我的想法是,它CAN,因为自从在容器类中的字段不是最终,没有知名度的保证.

二)但是,当我读到第16.3和关于"位,可以通过正确构造对象的最终现场达成变量 ",这是否意味着,由于容器C的实例被声明为final,我们DO有知名度保证在消耗()?

final Container c = new Container("Ted Dibiasi",10,1000000);

c)在Client类中声明对Container的引用为volatile,不会解决与引用相关的字段的可见性问题.

Den*_*kov 6

final Container c = new Container("Ted Dibiasi", 10, 1000000);
Run Code Online (Sandbox Code Playgroud)

如果c这里是最后一个字段Thread1而不是局部变量,那么Java语言规范的引用适用于这个最终字段c:

当构造函数完成时,对象被认为是完全初始化的.在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值.

最终字段的使用模型很简单:在该对象的构造函数中设置对象的最终字段; 并且在对象的构造函数完成之前,不要在另一个线程可以看到的地方写入对正在构造的对象的引用.如果遵循此原因,那么当另一个线程看到该对象时,该线程将始终看到该对象的最终字段的正确构造版本.它还将看到那些最终字段引用的任何对象或数组的版本,这些字段至少与最终字段一样是最新的.

虽然这里的措辞含糊不清,但我认为" 正确初始化的值 "和" 最新的最终字段 "意味着如果你将构造函数传递cThread2外部Thread1,Thread2它将始终看到一个完整构造的Container实例及其字段初始化.