volatile作用与底层原理
volatile
volatile作用:
可见性 -》lock-> 缓存一致性协议【MESI】
有序性-》防止指令重排
如何实现的?
在JMM内存模型中:
两个线程所占的寄存器中都有一个i值,都是从主存中读到并拷贝到各自的缓存,并最终到达寄存器中。
先看一下不使用volatile会有什么后果:
当核心1修改i值为8时,此时只是寄存器中发生了修改,但是没有及时刷到主存中。
核心2从寄存器中拿到的i值还是2,这就是缓存不可见性的问题。
volatile原语规定,在对临界值 : i进行赋值操作时,对其余的缓存行中的i值设置为失效的。
每个处理器通过嗅探在总线上传播的数据来判断检查出自己的缓存是不是过期了。
当处理器发现自己的缓存对应的内存地址被修改,就会设置自己的缓存为过期状态。
当处理器要对这个数据修改时,就会从主存中重新获取值。
这样一来,某个线程要对i值进行操作时,就必须要等修改的线程将i值刷回主存中,这个线程再去拿i,这样就能保证线程至少不会出现脏读。这就是缓存一致性协议-MESI。
考虑一下底层是怎么做的
这里是使用了锁机制。当使用volatile原语,会在JVM指令添加一个 #LOCK指令前缀,底层其实是给缓存行添加锁。
缓存行有四种状态:
- M:修改
- E:独占
- S:共享
- I:失效
补充:伪共享
一个缓存行是64个字节。要避免出现伪共享情况。即一个缓存行有多个共享数据。其中一个线程在高频的修改其中一个共享数据,那么别的共享数据就会频繁失效,非常影响性能。
可以多设置几个变量吗,将一个缓存行占满。
比如:long x,y; 线程1执行x1000,0000次累加,线程2执行y的1000,0000次累加。那么,两个缓存行中的y,x变量会一直失效重新取。
改为:long x1,x2,x3,x4,x5,x6,x7,x8;缓存行占满后,y值另起一个缓存行。
disrupter工具,可以消除伪共享问题。