安全发布和不可变与有效不可变的优势

2022-09-01 19:25:18

我正在重读Java并发实践,我不确定我是否完全理解了关于不可变性和安全发布的章节。

书中说的是:

任何线程都可以安全地使用不可变对象,而无需额外的同步,即使不使用同步来发布它们也是如此。

我不明白的是,为什么有人(有兴趣使他的代码正确)不安全地发布一些引用?

如果对象是不可变的,并且它以不安全的方式发布,我理解任何其他线程获取对该对象的引用都会看到其正确的状态,因为适当的不可变性(使用字段等)提供了保证。final

但是,如果发布不安全,另一个线程可能仍然可以看到发布后或以前的引用,而不是对不可变对象的引用,这在我看来是没有人想要的。null

如果使用安全发布来确保所有线程都能看到新引用,那么即使对象实际上是不可变的(没有字段,但没有办法将它们静音),那么一切都再次安全。正如书中所说:final

安全发布有效不可变对象可以由任何线程安全地使用,而无需额外的同步。

那么,为什么不可变性(与有效的不可变性)如此重要呢?在什么情况下会想要不安全的出版物?


答案 1

出于两个原因,最好设计不需要同步的对象:

  1. 对象的用户可能会忘记同步。
  2. 尽管开销很小,但同步也不是免费的,尤其是在对象不经常使用并且由许多不同的线程使用的情况下。

由于上述原因非常重要,因此最好学习有时困难的规则,并且作为编写者,创建不需要同步的安全对象,而不是希望代码的所有用户都记得正确使用它。

还要记住,作者并不是说该对象被不安全地发布,它是在没有同步的情况下安全发布的。

至于你的第二个问题,我刚刚检查了一下,这本书并没有向你保证另一个线程将始终看到对更新对象的引用,只是如果它这样做,它将看到一个完整的对象。但我可以想象,如果它通过另一个(?)对象的构造函数发布,那将是甜蜜的。不过,这确实有助于解释所有情况。Runnable

编辑:

有效地不可变和不可变有效不可变和不可变之间的区别在于,在第一种情况下,您仍然需要以安全的方式发布对象。对于真正不可变的对象,这是不需要的。因此,真正不可变的对象是首选,因为它们由于我上面提到的原因而更容易发布。


答案 2

那么,为什么不可变性(与有效的不可变性)如此重要呢?

我认为重点是,真正不可变的对象以后更难打破。如果您已声明一个字段,则它是最终的句点。您必须删除 才能更改该字段,这应该会响起警报。但是,如果你最初省略了,有人可能会不小心添加一些更改字段的代码,然后繁荣 - 你搞砸了 - 只有一些添加的代码(可能在子类中),没有对现有代码进行任何修改。finalfinalfinal

我还假设显式不可变性使(JIT)编译器能够执行一些优化,否则这些优化将很难或不可能证明其合理性。例如,使用字段时,运行时必须保证与写入和读取线程的关系。在实践中,这可能需要内存屏障,禁用无序执行优化等 - 即性能下降。但是,如果对象是(深度)不可变的(仅包含对其他不可变对象的引用),则可以在不破坏任何内容的情况下放宽要求:只需写入和读取单个引用,而不是整个对象图,就可以保证之前发生的关系。volatilefinal

因此,显式不可变性使程序更简单,因此人类更容易推理和维护,并且更容易使计算机以最佳方式执行。随着对象图的增长,这些好处呈指数级增长,即对象包含包含对象的对象 - 如果一切都是不可变的,那么这一切都很简单。当需要可变性时,将其本地化到严格定义的位置并保持其他所有内容不可变仍然会带来很多这些好处。