问题 最终与波动的保证书,以安全发布对象


从Java并发实践中的书:

要安全地发布对象,两者都要   引用对象和   必须使对象的状态可见   其他线程同时。一个   正确构造的对象可以   安全发布者:

  • 从静态初始化程序初始化对象引用

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

  • 将对它的引用存储到正确构造的最终字段中   目的

  • 将对它的引用存储到由a正确保护的字段中
      锁。

我的问题是:

  1. 子弹点2和3之间有什么区别?我对两者之间的区别感兴趣 volatile 方法和 final 在安全发布对象方面的方法。
  2. 最后一场是什么意思 一个正确构造的对象 在第3点?在开始项目符号之前,作者已经提到过他们正在谈论一个正确构造的对象(我假设它不会让它出现 this 参考逃跑)。但他们为什么再次提到正确构造的物体呢?

5323
2018-02-09 17:03


起源



答案:


子弹点2和3之间有什么区别?

  • volatile 基本上意味着对该字段的任何写入都将从其他线程中可见。因此,当您将字段声明为volatile时: private volatile SomeType field;,如果构造函数写入该字段,则可以保证: field = new SomeType();,这个赋值将被随后尝试读取的其他线程可见 field
  • final 具有非常相似的语义:如果您有最终字段,则可以保证: private final SomeType field; 写入该字段(在声明或构造函数中): field = new SomeType(); 将不会被重新编码,并且其他线程将可见 如果对象被正确发布 (即没有逃脱 this 例如)。

显然,主要不同的是,如果该字段是最终的,您只能分配一次。

在第3点中,正确构造的物体的最终场是什么意思?

例如,如果你让 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();
    }
}

9
2018-02-09 19:09



如果在构造之后我改变了像字段所指的对象的状态 field.setX(new X())当我们将其声明为volatile时,是否还保证其他线程可以看到该更改?或者它只提供初始化安全性?在这种情况下最终怎么样? - Geek
不,只 field 为您提供(重新)分配的可见性保证。除非 x 也很不稳定, field.x = new X(); (或你的二传手示例)没有提供这样的保证。 - assylias
同样适用于最终,如果所有成员都必须得到推论 field 也是最终的,是不可改变的, field 是不可变的,因此是线程安全的。 - assylias
@assylias我读了几次你的答案,但仍然无法得到它。由于字段是最终的,不会有任何重新排序,为什么新线程不会看到“this”的“字段”属性?你是说,这是因为构造函数还没有完成? - Abidi
@Abidi你是对的 - 改变了榜样。 - assylias


答案:


子弹点2和3之间有什么区别?

  • volatile 基本上意味着对该字段的任何写入都将从其他线程中可见。因此,当您将字段声明为volatile时: private volatile SomeType field;,如果构造函数写入该字段,则可以保证: field = new SomeType();,这个赋值将被随后尝试读取的其他线程可见 field
  • final 具有非常相似的语义:如果您有最终字段,则可以保证: private final SomeType field; 写入该字段(在声明或构造函数中): field = new SomeType(); 将不会被重新编码,并且其他线程将可见 如果对象被正确发布 (即没有逃脱 this 例如)。

显然,主要不同的是,如果该字段是最终的,您只能分配一次。

在第3点中,正确构造的物体的最终场是什么意思?

例如,如果你让 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();
    }
}

9
2018-02-09 19:09



如果在构造之后我改变了像字段所指的对象的状态 field.setX(new X())当我们将其声明为volatile时,是否还保证其他线程可以看到该更改?或者它只提供初始化安全性?在这种情况下最终怎么样? - Geek
不,只 field 为您提供(重新)分配的可见性保证。除非 x 也很不稳定, field.x = new X(); (或你的二传手示例)没有提供这样的保证。 - assylias
同样适用于最终,如果所有成员都必须得到推论 field 也是最终的,是不可改变的, field 是不可变的,因此是线程安全的。 - assylias
@assylias我读了几次你的答案,但仍然无法得到它。由于字段是最终的,不会有任何重新排序,为什么新线程不会看到“this”的“字段”属性?你是说,这是因为构造函数还没有完成? - Abidi
@Abidi你是对的 - 改变了榜样。 - assylias


出版的影响没有区别 volatile VS final, 除了那个 final 只能在构造函数中设置一次,因此您阅读的内容永远不会改变。

我相信一个 正确构造的对象 确实是你所指的,一个对象 this 引用没有转义它的构造函数,并以安全的方式发布到它所使用的线程。


1
2018-02-09 19:10