我最近开始学习C#并直接学习内存模型。 C#和Java在volatile
字段的读写方面有类似(尽管可能不相同)的线程安全保证。但与Java中对final
字段的写入不同,对C#中的readonly
字段的写入不提供任何特定的线程安全保证。考虑线程安全如何在C#中工作,让我怀疑final
字段在Java中的行为方式是否有任何真正的优势。
三年前我了解了final
的重要性。我问this question并得到了我接受的详细答案。但现在我认为这是错误的,或者至少是无关紧要的。我仍然认为字段应尽可能final
,而不是出于普遍认为的原因。
在构造函数返回后,final
字段的值保证对任何其他线程可见。但是对象本身的引用必须以线程安全的方式发布。 如果安全发布参考,则final
的可见性保证会变得多余。
我考虑过它与public static
字段有关的可能性。但从逻辑上讲,类加载器必须同步类的初始化。同步使final
的线程安全性变得多余。
所以我提出了一个异端的观点,即final
的唯一真正价值在于使不变性自我记录和自我实施。在实践中,私有非final字段(,特别是数组元素)是完全线程安全的,只要它们在构造函数返回后不被修改。
我错了吗?
编辑:释义Java Concurrency in Practice中的第3.5节
不正确发布的对象可能会出现两件事。其他线程可以看到引用的陈旧值,因此即使已设置值,也会看到空引用或其他旧值。但更糟糕的是,其他线程可以看到参考的最新值,但是对象状态的陈旧值。
我理解final
字段如何解决第二个问题,但不是第一个问题。到目前为止,最高投票的答案认为第一个问题不是问题。
编辑2:这个问题源于术语的混淆。
就像a similar question的提问者一样,我总是理解“安全出版物”这个术语"这意味着对象的内部状态和对对象本身的引用都保证对其他线程可见。为了有利于这个定义,Effective Java引用了Goetz06,3.5.3来定义"安全发布" as(强调补充)
将这样的对象引用从一个线程传输到其他线程
同样赞成这个定义,请注意上面的Java Concurrency in Practice一节中提到的可能过时的引用被称为"不正确地发布。"
无论你怎么称呼它,我都不认为不安全地发布对不可变对象的引用可能是有用的。但根据this answer,它可以。 (给出的示例是原始值,但相同的原则可以应用于参考值。)
答案 0 :(得分:4)
但是对象本身的引用必须以线程安全的方式发布。 如果安全发布参考,则
final
的可见性保证会变得多余。
第一句话错了;因此,第二个是无关紧要的。在存在其他安全发布技术(如同步或final
)时,volatile
可能是多余的。但是,不可变对象的意义在于它们本身 线程安全,这意味着无论引用如何发布,它们都将以一致的状态被看到。因此,您首先不需要其他技术,至少就安全出版而言。
编辑:OP正确指出围绕术语"安全发布"存在一些含糊之处。在这种情况下,我指的是对象内部状态的一致性。在我看来,影响参考的可见性问题是一个有效但独立的问题。
答案 1 :(得分:0)
我已经多次阅读过您的问题但仍有一些问题低估了它。我将尝试增加另一个答案 - 这实际上是正确的。 Java中的final
所有关于重新排序(或者在您调用它之前发生)。
首先,这由JLS
保证,请参阅Final Field Semantics。在该示例中,您可以看到其他线程可以保证正确看到单个final字段,而另一个则不能。 JLS是正确的,但在当前的实现下,单个字段为final
就足够了 - 进一步阅读。
对构造函数中的最后一个字段的每次写入后跟两个memory barriers
- StoreStore
和LoadStore
(因此发生之前的名称);因为最终字段的存储将在读取到同一字段之前发生 - 通过内存屏障保证。
但是当前的实现没有这样做 - 意味着在每次写入最终的之后都不会发生内存障碍 - 它们发生在构造函数的末尾,请参阅this 。你会看到这一行非常重要:
_exits.insert_mem_bar(Op_MemBarRelease, alloc_with_final());
Op_MemBarRelease
是实际LoadStore|StoreStore
屏障,因此在当前实施下,只需要一个字段为所有其他字段安全也发表了。但是,当然,这样做需要您自担风险。
volatile
不会使出版物足够 - 因为它不会引入必要的障碍,你可以多读一点here。
请注意,引用发布不是问题,只是因为JLS说:Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
。