25

来自 Java concurrency in practice 一书:

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

  • 从静态初始化器初始化对象引用

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

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

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

我的问题是:

  1. 要点 2 和 3 之间有什么区别?我对安全发布对象方面的volatile方法和方法之间的区别感兴趣。final
  2. 他在第 3 点中正确构造对象的最终字段是什么意思?在开始项目符号点之前,作者已经提到他们正在谈论一个正确构造的对象(我假设它不会让this引用 escape )。但是为什么他们又一次提到了正确构造的对象?
4

2 回答 2

18

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

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

显然,主要的不同是如果字段是 final 的,你只能分配一次。

他在第 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();
    }
}
于 2013-02-09T19:09:41.137 回答
2

volatilepublish与的效果没有区别final,除了final只能在构造函数中设置一次,因此您阅读的内容永远不会改变。

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

于 2013-02-09T19:10:31.467 回答