一、简介
1.Volatile内存语义
1.当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值立即刷新回主内存中。
2.当读一个volatile变量时,JMM会把该线程对应的本地内存设置为无效,重新回到主内存中读取最新共享变量。
3.所以volatile的写内存语义是直接刷新到主内存中,读的内存语义是直接从主内存中读取。
2.两大特性
1.可见性 :写完后立即刷新回主内存并及时发出通知,大家可以去主内存拿最新版,前面的修改对后面所有线程可见!
2.禁止指令重排:重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段,有时候会改变程序语句的先后顺序。不存在数据依赖关系,可以重排序;存在数据依赖关系,禁止重排序。但重排后的指令绝对不能改变原有的串行语义!这点在并发设计中必须要重点考虑!
3.上面的禁止指令重排是基于内存屏障实现的:
粗分两种
1.读屏障:在读指令之前插入读屏障,让工作内存或CPU高速缓存当中的缓存数据失效,重新回到玉内存中获取最新数据。
2.写屏障:在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中。
在Java层面的Unsafe类就有以下的native方法:
对应的C代码如下:
二、happens-before之volatile规则
继续查看OrderAccess.hpp
就能发现细分的内存屏障是以下的四种:
四种内存屏障的总结说明:
在每个volatile读操作的后面插入一个LoadLoad屏障:禁止处理器把上面的volatile读与下面的普通读重排序。
在每个volatile读操作的后面插入一个LoadStore屏障:禁止处理器把上面的volatile读与下面的普通写重排序。
在每个volatile写操作的前面插入一个StoreStore屏障:可以保证在volatile写之前,其前面的所有普通写操作都已经刷新到主内存中。
在每个volatile写操作的后面插入一个 StoreLoad屏障:作用是避免volatile写与后面可能有的volatile读/写操作重排序。
字节码层次的体现:
三、volatile的特性
1.可见性
如果去掉volatile关键字,那么程序就会发生阻塞。
public class volatileCasy {
static volatile boolean flag = true;
public static void main(String[] args)