为什么Object成员变量在Java中不能是final和volatile?

Alb*_*Alb 35 java syntax multithreading

如果在类中我有一个ConcurrentHashMap实例,它将被多个线程修改和读取,我可能会这样定义:

public class My Class {

    private volatile ConcurrentHashMap<String,String> myMap = new ConcurrentHashMap<String,String>();
...
}
Run Code Online (Sandbox Code Playgroud)

添加final到myMap字段会导致错误,说我只能使用final或volatile.为什么不能两者兼而有之?

Mic*_*rdt 29

volatile仅与变量本身的修改有关,而与其引用的对象无关.拥有一个final volatile字段是没有意义的,因为无法修改最终字段.只要声明该字段final,它应该没问题.

  • @johnterpreneur:这不正确; 最终字段只能在构造对象*期间分配给*,这几乎就是"不可变"的定义. (4认同)
  • @johntrepreneur:啊,现在我看到了误解发生的地方.正如我在答案中所写,字段与其引用的对象之间存在差异 - 这是您显然缺少的一个重要区别(或使用错误的术语).`final`表示*字段本身*不能被修改,但它仍然可以引用一个可变对象.字段的内容只是一个引用(对于非基本类型),而不是对象. (2认同)
  • @shmosel Java 标准没有定义内存屏障。对于*both*、`final` 和`volatile` 变量来说,该值的读取和写入之间存在*happens-before* 关系。线程不会通过执行相同值的后续读取来获得额外的保证。像 HotSpot JVM 这样的实现可能会在每次读取“易失性”变量时保守地插入一个屏障,但您仍然无法依赖它构建正确的程序。当值没有改变时。因此,对于不会再次更改的“final”变量,“volatile”没有任何意义。 (2认同)

Vic*_*kin 27

这是因为Java内存模型(JMM).

基本上,当你声明对象字段时,final你需要在对象的构造函数中初始化它,然后final字段不会改变它的值.并且JMM承诺在ctor完成之后,任何线程都将看到相同(正确)的final字段值.因此,您不需要使用显式同步,例如synchronizeLock允许所有线程看到正确的final字段值.

当您声明对象的字段时volatile,字段的值可以更改,但是仍然从任何线程读取的每个值都会看到写入它的最新值.

所以,finalvolatile实现相同的目的-对象的字段值的可视性,但是第一具体用于可变可以仅被分配给一次和第二用于可以改变许多次的变量.

参考文献:

  • "最终和不稳定的"达到了同样的目的......"我认为这会把车推到马前.`final`的_purpose_是声明不能分配变量或字段.`volatile`的_purpose_告诉编译器通过检查代码无法推断出该值.Java内存模型对这两种变量中每种变量的可见性有特殊规则这一事实是次要的,它们不重叠. (3认同)

Sun*_*B M 8

因为volatilefinal在Java两种极端的两端

volatile 表示变量绑定到更改

final 意味着变量的值永远不会改变