Java 最终vs volatile guaranntee w.rt安全发布对象

Java 最终vs volatile guaranntee w.rt安全发布对象,java,java-memory-model,Java,Java Memory Model,摘自《Java并发实践》一书: 要安全地发布对象,两个 对对象和对象的引用 必须使对象的状态对用户可见 同时使用其他线程。A. 正确构造的对象可以 安全出版人: 从静态初始值设定项初始化对象引用 将对它的引用存储到volatile字段或原子引用中 将对它的引用存储到正确构造的 反对 将对它的引用存储到由 锁 我的问题是: 要点2和要点3之间有什么区别?我对volatile方法和final方法在对象安全发布方面的差异感兴趣 他所说的第三点中正确构造的物体的最终场是什么意思?在开始项目符号点之前

摘自《Java并发实践》一书:

要安全地发布对象,两个 对对象和对象的引用 必须使对象的状态对用户可见 同时使用其他线程。A. 正确构造的对象可以 安全出版人:

  • 从静态初始值设定项初始化对象引用

  • 将对它的引用存储到volatile字段或原子引用中

  • 将对它的引用存储到正确构造的 反对

  • 将对它的引用存储到由

我的问题是:

  • 要点2和要点3之间有什么区别?我对
    volatile
    方法和
    final
    方法在对象安全发布方面的差异感兴趣
  • 他所说的第三点中正确构造的物体的最终场是什么意思?在开始项目符号点之前,作者已经提到,他们讨论的是一个正确构造的对象(我假设它不会让
    this
    引用转义)。但他们为什么再次提到正确构造的对象呢
  • 要点2和要点3之间有什么区别

    • volatile
      基本上意味着对该字段的任何写入都可以从其他线程看到。因此,当您将字段声明为volatile时:
      private volatile SomeType字段,则可以保证如果构造函数写入该字段:
      field=newsometype(),此分配将由随后尝试读取
      字段的其他线程可见
    • final
      具有非常相似的语义:您可以保证,如果您有final字段:
      private final SomeType字段
      对该字段的写入(在声明或构造函数中):
      field=newsometype()将不会重新排序,并且将被其他线程看到(例如,
      this
      的转义)
    显然,主要区别在于,如果字段是final,则只能分配一次

    他所说的第三点中正确构造的物体的最终场是什么意思

    例如,如果您让
    this
    从构造函数中转义,那么最终语义提供的保证就消失了:观察线程可能会看到具有默认值的字段(对象为null)。如果对象构造正确,则不会发生这种情况


    人为的例子:

    class SomeClass{
        private final SomeType field;
    
        SomeClass() {
            new Thread(new Runnable() {
                public void run() {
                    SomeType copy = field; //copy could be null
                    copy.doSomething(); //could throw NullPointerException
                }
            }).start();
            field = new SomeType();
        }
    }
    

    发布
    volatile
    final
    的效果没有区别,只是
    final
    只能在构造函数中设置一次,因此您读取的内容永远不会更改


    我相信正确构造的对象确实是您所指的对象,其
    引用没有逃逸其构造函数,并且已以安全的方式发布到所使用的线程中。

    如果在构造之后,我更改由字段引用的对象的状态,如
    field.setX(new X())
    ,当我们将其声明为volatile时,这种更改是否也保证了其他线程可以看到?或者它只提供初始化安全性?在这种情况下,final如何?不,只有
    字段
    为您提供了(重新)分配的可见性保证。除非
    x
    也是易变的,
    field.x=new x()
    (或您的setter示例)不提供这样的保证。final也一样,如果
    字段
    的所有成员也是final并且是不可变的,
    字段
    是不可变的,因此是线程安全的。@assylias我读了几遍您的答案,但仍然无法得到它。既然字段是最终的,就不会有任何重新排序,为什么新线程看不到“this”的“field”属性呢?你是说,这是因为构造函数还没有完成吗?当使用
    final
    字段时,你甚至有了更多的保证:“[另一个线程]还将看到那些final字段引用的任何对象或数组的版本,它们至少与final字段一样是最新的。”()