彻底搞懂Volatile
大家都知道volatile有着三个特性,可见性,非原子性,还有个防止指令重排
可见性指的是一个线程对一个共享变量的修改,另一个变量也能读到修改的值
volatile比Synchronized的使用成本更低,它不会引起线程的上下文切换和调度
1. Volatile的定义
为了确保线程对同一共享变量能准确的和一致性的更新。一般情况是通过排他锁,而volatile更为方便,能使java线程内存模型确保所有线程对共享变量的一致性
我们先看一下JMM的内存模型,再回来说Volatile
JMM 对程序间变量的访问做出的规则
先read主内存的变量,再load到工作内存,use取出工作内存的值给执行引擎使用
assign把执行引擎的值赋值给工作内存,store把工作内存的传递给主内存,主内存再write写入
如果不用static变量,那么多个线程之间共享变量的修改,如何保持可见呢?这就是问题引出了volatile了
接着来说volatile如何保证可见性
其实只要反汇编加了volatile的代码,都会发现有lock指令
通过查IA-32架构软件开发者手册可知
lock前缀指令会在多核处理器下引发两件事
- 将当前处理器缓存行的数据写回主内存
如果缓存数据已经在工作内存了,它就会锁定这块内存区域得缓存,然后写回主内存,再以缓存一致性为基础,保证了其它线程得数据一致,这个操作就叫缓存锁定,它会阻止多个处理器同时修改共享变量
- 当一个处理器写回数据的时候,会使其它cpu缓存的这个数据失效
这个就不难理解了,缓存一致性得作用,通过总线嗅探是否缓存数据内存地址已经发生改变
为什么处理器不直接在内存工作,这样就可以大家不需要缓存了,直接从内存读取值就像redis一样
这个想法其实以前就想到了,在很久以前的处理机,他们都有一个总线锁的机制,在一个处理器修改的时候,会独占整个内存和所有相关的数据,其它人期间只能等待,但是这样效率太低了,全部锁住,那么其它人只要需要用到,都得卡住
缓存一致性MESI协议
为了保证各个处理器上得缓存是一致性得,每个处理器会通过嗅探在总线上传播得数据来看自己得值是不是被修改了,如果发现内存地址被修改,就会将当前缓存得数据设置为失效,如果还想操作这个数据,就要重新从内存读取,这样就保证了一致性
其实还有一点没分析,就是volatile得优化,但是本人能力有限日后再补上