前言
前面介绍顺序一致性模型时,我们提到了程序如果正确的同步就会具备顺序一致性,这里所说的同步泛指广义上的同步,其中包括就包括同步原语volatile,那么volatile声明的变量为什么就能保证同步呢?这又是如何实现的呢?今天就让我们一起来揭开这神秘的面纱,OK,开始我们今天的并发编程之旅吧。
volatile的特性
volatile特性可以理解为对volatile变量的单个读写,看成是使用了同一个锁对这些单个读写进行了同步,他们之间的执行效果是一样的;volatile变量自身具有下面2种特性:
原子性:对任意单个volatile变量的读-写具有原子性,但是对于volatile++这种复合操作就不保证其原子性了;
volatile写-读的内存语义
volatile写内存语义:当写一个volatile变量时,JMM会把线程对应的本地内存中的共享变量值刷新到主内存,如下图所示:
volatile读内存语义:当读一个volatile变量时,JMM会把线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量,如下图所示:
volatile内存语义的实现
接下来我们再看看volatile在JMM中是如何实现的,首先看下面这张volatile重排序规则表:

NO表示不允许进行重排序,例如当第一个操作是volatile读时,就不允许重排序,为了实现volatile内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序,下面我们再看看JMM中内存屏障的插入策略:
在每个volatile写操作的后面插入一个StoreLoad屏障。该屏障可以防止上面的volatile写与下面的volatile读或写操作发生重排序
在每个volatile读后面插入一个LoadStore屏障,该屏障可以禁止下面所有的普通写和上面的volatile读发生重排序
volatile写插入内存屏障后生成的指令序列:

volatile读插入内存屏障后生成的指令序列:

以上就是同步原语volatile的内存语义,通过这篇文章可以了解到底层是如何保证同步的,希望你能有所收获,感谢阅读!!!

原文始发于微信公众号(Justin的后端书架):