窥探真相:volatile 可见性实现原理

前言

并发编程中,经常使用到syncronized和volatile同步元语。相比较syncronized,volatile可以说是Java虚拟机提供的轻量级同步机制,因为它不会引起线程上下文切换和调度。使用volatile的一个关键目的是保证共享变量的可见性。本文将从CPU指令的角度了解Volatile如何实现共享变量的可见性

CPU存储层次构成

众所周知,由于CPU运行速度非常快,而主内存相对来说很慢,为了提高CPU运行效率,CPU并不直接访问主内存,两者之间设计有多层缓存结构。
下图是来至《深入理解计算机系统》书中关于CPU的高手缓存层次结构:

image.png

JAVA内存模型(Java Memory Model)

JAVA内存模型是JAVA虚拟机用来屏蔽硬件和操作系统内存读取差异,以达到各个平台下都能达到一致的内存访问效果。
JAVA内存模型和CPU存储结构对应关系


image.png

理想中的CPU存储结构是所有CPU共享同一个Cache
这样,当其中一个CPU进行写操作,而另一个CPU进行读操作,总是能读到正确的值。
但是,会极大的降低系统的运算速度,因为所有CPU均需要串行的访问Cache以获取数据,大部分时间均在等待Cache使用权。

如果引入多个Cache,就会涉及到Cache的一致性问题。


image.png

所以,Cache的一致性问题,不是因为多CPU导致,而是多Cache导致。

实现volatile依赖的CPU指令

public class Test  {
    private static volatile int a = 1;
    
    public static void test() {
            a = 2;   
    }

    public static void main(String [] args) {
        test();
    }
}

我们添加hsdis插件到JRE的lib目录后,可以对上述代码进行反汇编:

javac Test.java

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly Test
 
参数+PrintAssembly 的意思是打印出汇编代码

结果如下:

  0x000000011a5ddf25: callq  0x000000010cb439f0  ;   {runtime_call}
  0x000000011a5ddf2a: vzeroupper
  0x000000011a5ddf2d: movl   $0x5,0x270(%r15)
  0x000000011a5ddf38: lock addl $0x0,(%rsp)
  0x000000011a5ddf3d: cmpl   $0x0,-0xd4ec2e7(%rip)        # 0x000000010d0f1c60

lock 指令就是CPU实现volatile可见性的秘密所在。通过查IA-32架
构软件开发者手册。

8.1.4 Effects of a LOCK Operation on Internal Processor Caches

For the Intel486 and Pentium processors, the LOCK# signal is always asserted on the bus during a LOCK operation,
even if the area of memory being locked is cached in the processor.
For the P6 and more recent processor families, if the area of memory being locked during a LOCK operation is
cached in the processor that is performing the LOCK operation as write-back memory and is completely contained
in a cache line, the processor may not assert the LOCK# signal on the bus. Instead, it will modify the memory location internally and allow it’s cache coherency mechanism to ensure that the operation is carried out atomically. This
operation is called “cache locking.” The cache coherency mechanism automatically prevents two or more processors that have cached the same area of memory from simultaneously modifying data in that area.

对于Intel486和
Pentium处理器,在锁操作时,总是在总线上声言LOCK#信号。但在P6和目前的处理器中,如果
访问的内存区域已经缓存在处理器内部,则不会声言LOCK#信号。相反,它会锁定这块内存区
域的缓存并回写到内存,并使用缓存一致性机制来确保修改的原子性,此操作被称为“缓存锁定”,缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据。

.

in the Pentium and P6 family processors, if through snooping one processor
detects that another processor intends to write to a memory location that it currently has cached in shared state,
the snooping processor will invalidate its cache line forcing it to perform a cache line fill the next time it accesses
the same memory location.

在Pentium和P6 family处理器中,如果通过嗅探一个处理
器来检测其他处理器打算写内存地址,而这个地址当前处于共享状态,那么正在嗅探的处理
器将使它的缓存行无效,在下次访问相同内存地址时,强制执行缓存行填充
一个处理器的缓存回写到内存会导致其他处理器的缓存无效

上述引用总结为volatile的两条实现原则:

  • 对缓存行加锁内容的修改会导致修改后的值马上回写内存

  • 一个处理器的缓存回写到内存会导致其他处理器的缓存无效

image.png

缓存一致性协议

一致性缓存:所有缓存副本中的值都相同,多个CPU处理器共享缓存并且更改共享数据时,更改必须广播到所有缓存副本。
在处理器中,嗅探是一致性缓存的常见的机制。工作原理是通过总线监听所有的共享数据的操作,当修改共享数据的事件发生时,所有监听处理器都会检查是否存在当前共享数据的副本,如果存在,监听处理器会执行刷新或者直接失效缓存副本操作。

实现方式

缓存将具有三个额外的位:
V(可用)D(脏位,表示高速缓存中的数据与内存中的数据不同)S(共享)

读未命中:
CPU_A本地缓存读未命中,会广播到监听总线上,其他所有CPU监听处理器会检查,如果缓存了该地址,并且缓存处于“D(脏位)”,将状态改成有效,同时发送副本到请求节点。

写未命中:
CPU_A尝试更新本地缓存,但是更新并不在主存中。其他所有CPU监听处理器
可确保将其他高速缓存中的所有副本都设置为“无效”。

以上是主要的场景,具体实现有 MESI 协议等。

总结

基于CPU缓存一致性协议,JVM实现了volatile的可见性。当一个变量被volatile修饰时,那么对它的修改会立刻刷新到主存,当其它线程需要读取该变量时,会去内存中读取新值。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容