volatile对原子性的保证真的是非常的有限,其实主要就是32位jvm中的long/double类型变量的赋值操作是不具备原子性的,加上volatile就可以保证原子性了
volatile boolean isRunning = true;
线程1:
Release屏障
isRunning = false;
Store屏障 => 对于之前的讲解,更进了一步,原理,没有过多的牵扯到内存屏障的一些东西,可见性和有序性,主要都是基于各种内存屏障来实现的
线程2:
Load屏障
while(isRunning) {
Acquire屏障
// 代码逻辑
}
在volatile变量写操作的前面会加入一个Release屏障,然后在之后会加入一个Store屏障,这样就可以保证volatile写跟Release屏障之前的任何读写操作都不会指令重排,然后Store屏障保证了,写完数据之后,立马会执行flush处理器缓存的操作
在volatile变量读操作的前面会加入一个Load屏障,这样就可以保证对这个变量的读取时,如果被别的处理器修改过了,必须得从其他处理器的高速缓存(或者主内存)中加载到自己本地高速缓存里,保证读到的是最新数据;
在之后会加入一个Acquire屏障,禁止volatile读操作之后的任何读写操作会跟volatile读指令重排序
跟之前讲解的volatie读写内存屏障的知识对比一下,其实你看一下是类似的意思的
那个Acquire屏障其实就是LoadLoad屏障 + LoadStore屏障,Release屏障其实就是StoreLoad屏障 + StoreStore屏障
好像有点不太一样,对吧?
其实不要对内存屏障这个东西太较真,因为说句实话,不同版本的JVM,不同的底层硬件,都可能会导致加的内存屏障有一些区别,所以这个本来就没完全一致的。你只要知道内存屏障是如何保证volatile的可见性和有序性的就可以了
看各种并发相关的书和文章,对内存屏障到底是加的什么屏障,莫衷一是,没有任何一个官方权威的说法,因为这个内存屏障太底层了,底层到了涉及到了硬件,硬件不同对内存屏障的实现是不一样的
内存屏障这个东西,大概来说,其实就是大概的给你说一下这个意思,尤其是Release屏障,Store屏障和Load屏障还好理解一些,比较简单,Acqurie屏障,莫衷一是,我也没法给你一个官方的定论
具体底层的硬件实现
如果你一定 要杠到底,到底加的准确的屏障是什么?到底是如何跟上下的指令避免重排的,你自己去研究吧。我之前看过很多的资料,做过很多的研究,硬件对这个东西的实现和承诺,莫衷一是,没有标准和官方定论。
两点:volatile读写前后会加屏障,避免跟前后的读写操作发生指令重排
volatile和synchronized保证可见性和有序性,原来都是通过各种内存屏障来实现的,因为加了内存屏障,就会有一些特殊的指令和实现,就可以保证可见性和有序性了,有序性在几个阶段的指令重排的问题
内存屏障对应的底层的一些基本的硬件级别的原理,也都讲清楚了