如果在类中我有一个ConcurrentHashMap实例,该实例将被多个线程修改和读取,我可能会这样定义:
public class My Class {
private volatile ConcurrentHashMap<String,String> myMap = new ConcurrentHashMap<String,String>();
...
}
将final
添加到 myMap 字段会导致错误,指出我只能使用 final 或易失性。为什么不能两者兼而有之?
volatile
只与变量本身的修改相关,而不与它所引用的对象相关。拥有final volatile
字段是没有意义的,因为最终字段无法修改。只需声明字段final
,它应该没问题。
Java内存模型(JMM(。
本质上,当您将对象字段声明为 final
时,您需要在对象的构造函数中初始化它final
然后字段不会更改其值。JMM承诺,在ctor完成后,任何线程都将看到final
字段的相同(正确(值。因此,无需使用显式同步(如 synchronize
或 Lock
(即可允许所有线程查看final
字段的正确值。
当您将对象的字段声明为 volatile
时,字段的值可以更改,但每次从任何线程读取值都会看到写入的最新值。
因此,final
和volatile
达到相同的目的 - 对象的字段值的可见性,但第一个是专门用于一个变量只能分配给一次,第二个用于可以多次更改的变量。
引用:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4
因为volatile
和final
是Java中的两个极端
volatile
表示变量绑定到更改
final
意味着变量的值永远不会改变
volatile
用于变量,在某些情况下,它们的值可能会改变,否则不需要volatile
,final
意味着变量可能不会改变,所以不需要volatile
。
您的并发问题很重要,但使HashMap
不稳定并不能解决问题,为了处理并发问题,您已经使用了 ConcurrentHashMap
。
volatile
字段为您提供了更改时会发生什么的保证。(没有可能引用的对象(
无法更改final
字段(可以更改字段引用的内容(
两者兼而有之是没有意义的。
volatile
修饰符保证所有读取和写入都直接进入主内存,即就像变量访问几乎进入synchronized
块一样。这与无法更改的最终变量无关。
因为它没有任何意义。易失性会影响对象的引用值,而不是对象的字段/等。
在您的情况下(您有并发映射(,您应该执行字段final
。
在多线程环境中,不同的线程将从主内存中读取变量并将其添加到 CPU 缓存中。这可能会导致两个不同的线程对同一变量进行更改,同时忽略彼此的结果。在此处输入图像描述
我们使用单词volatile 来表示变量将保存在主内存中并从主内存中读取。因此,每当线程想要读/写变量时,它都会从主内存中完成,本质上使变量在多线程环境中安全。
当我们使用 final 关键字时,我们表示变量不会改变。如您所见,变量是否不可更改,那么多个线程是否使用它并不重要。没有线程可以更改变量,因此即使变量在不同时间保存到 CPU 缓存中,并且线程也会在不同的时间使用此变量,因为它仍然可以,因为变量只能被读取。