Java并发——volatile
CPU的内存模型如下:
这种模式下,存在多核间缓存数据不一致的问题。为了解决这种问题,有2种硬件策略。
1.当一个CPU从主存读取缓存数据时,总线阻塞;
2.基于缓存一致性协议,当一个CPU读取到共享缓存时,如果某个CPU对共享缓存发生变更操作,会通知其它CPU缓存无效,触发其它CPU重新读主存。
JMM抽象的内存模型如下:
JMM是对硬件平台内存模型的抽象。
这个模型解释了JVM在内存中存取变量的动作。
因为只是对底层的抽象,出于性能考虑,存在一致性与指令重排等一些问题。解决这些问题有如下策略:
1.基于内存屏障,在读写共享变量的前后加入相应的屏障指令,阻止共享变量前后的指令重排;
2.基于缓存一致性协议,确保共享变量的写会及时通知所有的CPU。
内存屏障分类如下:
JMM定义的指令重排序规则:
volatile基于内存屏障来保证可见性与阻止指令重排(HB关系)。
- 在每个volatile写操作的前面插入一个StoreStore屏障;
- 在每个volatile写操作的后面插入一个StoreLoad屏障;
- 在每个volatile读操作的后面插入一个LoadLoad屏障;
- 在每个volatile读操作的后面插入一个LoadStore屏障。
StoreStore屏障:禁止上面的普通写和下面的volatile写重排序
StoreLoad屏障:防止上面的volatile写与下面可能有的volatile读/写重排序
LoadLoad屏障:禁止下面所有的普通读操作和上面的volatile读重排序
LoadStore屏障:禁止下面所有的普通写操作和上面的volatile读重排序
从volatile引申出一个伪共享的问题。
现在大部分计算机的缓存行是64个字节的,当多线程修改相互独立的变量时,如果这些变量共享同一个缓存行,就会影响彼此的性能。
如下图所示:
core1和core2修改同一个缓存行的不同变量,需要竞争缓存行的所有权,这种竞争会带来性能损耗。
在Java8中,要避免这种伪共享,可以如下配置:
1.在jvm参数上增加:-XX:-RestrictContended
2.在类,或者字段上增加注解:@Contended
参考: