为什么在Java中对象成员变量不能同时是final和volatile?
如果在一个类中,我有一个ConcurrentHashMap实例,它将被多个线程修改和读取,我可以这样定义:为什么在Java中对象成员变量不能同时是final和volatile?,java,multithreading,syntax,Java,Multithreading,Syntax,如果在一个类中,我有一个ConcurrentHashMap实例,它将被多个线程修改和读取,我可以这样定义: public class My Class { private volatile ConcurrentHashMap<String,String> myMap = new ConcurrentHashMap<String,String>(); ... } 公共类我的类{ 私有易失性ConcurrentHashMap myMap=新ConcurrentHa
public class My Class {
private volatile ConcurrentHashMap<String,String> myMap = new ConcurrentHashMap<String,String>();
...
}
公共类我的类{
私有易失性ConcurrentHashMap myMap=新ConcurrentHashMap();
...
}
将
final
添加到myMap字段会导致一个错误,即我只能使用final或volatile。为什么不能两者都是?volatile
用于在某些情况下其值可能会改变的变量,否则就不需要volatile
,而final
意味着变量可能不会改变,因此不需要volatile
您的并发关注点很重要,但是使
HashMap
易失性并不能解决问题,为了处理并发问题,您已经使用了ConcurrentHashMap
,因为易失性
和最终
是Java中的两个极端
volatile
表示变量绑定到更改
final
表示变量的值永远不会改变,因为它没有任何意义。Volatile影响对象引用值,而不是对象的字段/etc
在您的情况下(您有并发映射),您应该执行字段
final
volatile
仅与变量本身的修改相关,而与它引用的对象无关。使用final volatile
字段没有任何意义,因为final字段无法修改。只需声明字段final
,就可以了。volatile
修饰符保证所有读写都直接进入主内存,即变量访问几乎进入同步
块。这与不能更改的最终变量无关。这是因为Java内存模型(JMM)
本质上,当您将对象字段声明为final
时,您需要在对象的构造函数中初始化它,然后final
字段将不会更改它的值。JMM承诺在ctor完成后,任何线程都会看到与final
字段相同(正确)的值。因此,您不需要使用显式同步,例如synchronize
或Lock
来允许所有线程看到final
字段的正确值
当您将对象的字段声明为volatile时,字段的值可以更改,但从任何线程读取的每一个值都会看到写入该字段的最新值
因此,final
和volatile
实现了相同的目的——对象字段值的可见性,但第一个字段值专门用于变量,第二个字段值只能指定一次,第二个字段值用于可多次更改的变量
参考资料:
volatile
字段为您提供更改时发生的情况的保证。(无可能是引用的对象)
无法更改final
字段(可以更改字段引用的内容)
两者都有关系是没有意义的。这对我来说解释很好,但是我不会说final用于“常量”值。这句话有点误导,尽管我理解你想说的。“最终和不稳定”达到了同样的目的…“我认为这是本末倒置。
final
的目的是声明不得分配变量或字段。volatile
的目的是告诉编译器不能通过检查代码来推断值。Java内存模型对这两种变量的可见性都有特殊的规则,这一事实与它们的非重叠用途无关。这是真正解决核心问题的唯一答案,“JMM承诺,在ctor完成后,任何线程都会看到final字段的相同(正确)值。”但提供的文档引用并不支持它,就像澄清您的评论“最终字段不能修改”;事实上,final字段是可变的,但是final关键字只允许赋值一次。@jonterpreneur:这是不正确的;最后的字段只能在对象的构造过程中分配,这几乎就是“不可变”的定义。@johntrepreneur:啊,现在我知道误解发生在哪里了。正如我在回答中所写的那样,字段和它所引用的对象之间存在差异——这是一个很重要的区别,但您显然忽略了这一点(或者使用了错误的术语)final
表示字段本身无法修改,但它仍然可以引用可变对象。字段的内容只是一个引用(对于非基元类型),而不是一个对象。@corsiKa这就是为什么在构造过程中对象不应该泄漏的原因。这个问题并不局限于final
字段…@shmosel Java标准没有定义内存障碍。对于final
和volatile
变量,该值的读取和写入之间存在一个before-before关系。线程不会通过执行相同值的后续读取来获得额外的保证。像HotSpot JVM这样的实现可能会在每次读取volatile
变量时保守地插入一个屏障,但您仍然无法依靠它构建正确的程序。当值没有改变时。因此,对于不再更改的final
变量,volatile
没有任何意义。您无法创建HashMap(或任何其他对象)volatile
。“volatile”关键字会影响变量,而不是变量可能引用的对象。出现这个问题是因为您在一个线程上构造对象,而这就是最初设置引用的位置,然后可能会读取该引用