Java 安全发布以及不可变与有效不可变的优势
我正在实际阅读Java并发性,我不确定我是否完全理解了关于不变性和安全发布的章节 书中说的是: 任何线程都可以安全地使用不可变对象,而无需额外的 同步,即使同步未用于发布 他们 我不明白的是,为什么有人(对修改代码感兴趣)会不安全地发布一些引用 如果对象是不可变的,并且它是不安全地发布的,我知道任何其他获得对象引用的线程都会看到它的正确状态,因为适当的不可变性(带有Java 安全发布以及不可变与有效不可变的优势,java,multithreading,immutability,Java,Multithreading,Immutability,我正在实际阅读Java并发性,我不确定我是否完全理解了关于不变性和安全发布的章节 书中说的是: 任何线程都可以安全地使用不可变对象,而无需额外的 同步,即使同步未用于发布 他们 我不明白的是,为什么有人(对修改代码感兴趣)会不安全地发布一些引用 如果对象是不可变的,并且它是不安全地发布的,我知道任何其他获得对象引用的线程都会看到它的正确状态,因为适当的不可变性(带有final字段等)提供了保证 但是,如果发布不安全,另一个线程可能仍然会看到null或发布后的前一个引用,而不是对不可变对象的引用,
final
字段等)提供了保证
但是,如果发布不安全,另一个线程可能仍然会看到null
或发布后的前一个引用,而不是对不可变对象的引用,在我看来,这似乎是没有人喜欢的
如果使用安全发布来确保所有线程都能看到新引用,那么即使对象实际上是不可变的(没有final
字段,但无法使它们静音),那么一切都是安全的。正如书中所说:
安全发布的有效不可变对象可以由
没有额外同步的任何线程
那么,为什么不变性(相对于有效不变性)如此重要?在什么情况下需要不安全的发布?出于两个原因,最好设计不需要同步的对象:
Runnable
?)对象的构造函数发布的,那么它将是甜蜜的。不过,这确实有助于解释所有案例
编辑:
有效不可变和不可变
有效不可变和不可变的区别在于,在第一种情况下,您仍然需要以安全的方式发布对象。对于真正不可变的对象,这是不需要的。因此,真正不可变的对象是首选对象,因为基于我上面提到的原因,它们更容易发布
那么,为什么不变性(相对于有效不变性)如此重要
我认为主要的一点是,真正不可变的对象以后很难被打破。如果您声明了一个字段final
,那么它就是final,句号。您必须删除final
,才能更改该字段,这会发出警报。但是,如果您最初没有使用final
,可能会有人不小心添加一些更改字段的代码,然后只使用一些添加的代码(可能在子类中),而不修改现有的代码
我还假设显式不变性使(JIT)编译器能够进行一些优化,否则很难或不可能证明这些优化是正确的。例如,当使用volatile
字段时,运行时必须保证与写入和读取线程的“发生在前”关系。实际上,这可能需要内存障碍、禁用无序执行优化等—也就是说,性能受到影响。但是如果对象是(深度)不可变的(只包含对其他不可变对象的final
引用),则可以在不破坏任何内容的情况下放宽要求:只需通过编写和读取一个引用而不是整个对象图来保证发生在之前的关系
因此,显式不变性使程序变得更简单,因此人类更容易推理和维护,计算机也更容易以最佳方式执行。这些好处随着对象图的增长而呈指数增长,即对象包含包含对象的对象-如果一切都是不可变的,那么一切都很简单。当需要可变性时,将其定位到严格定义的位置并保持其他所有内容不变仍然会带来很多好处。在读完第1-3章时,我遇到了与原始海报完全相同的问题。我认为作者本可以做得更好,更详细地阐述这一点 我认为区别在于,当不安全发布时,可以观察到有效不可变对象的内部状态处于不一致状态,而不可变对象的内部状态永远不会处于不一致状态 但是,我确实认为,如果引用未安全发布,则可以观察到对不可变对象的引用已过期/过时。在其他线程看到写入字段的最新值的情况下,“不安全发布”通常是合适的,但是让线程看到更早的值相对来说是无害的。一个主要的例子是
String
的缓存哈希值。第一次对字符串调用hashCode()
,它将计算一个值并缓存它。如果在同一字符串上调用hashCode()
的另一个线程可以看到第一个线程计算的值,那么它就不必重新计算哈希值(从而节省时间)