Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/358.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在Java中对象成员变量不能同时是final和volatile?_Java_Multithreading_Syntax - Fatal编程技术网

为什么在Java中对象成员变量不能同时是final和volatile?

为什么在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

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

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”关键字会影响变量,而不是变量可能引用的对象。出现这个问题是因为您在一个线程上构造对象,而这就是最初设置引用的位置,然后可能会读取该引用