volatile实现解析
Posted 水田如雅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile实现解析相关的知识,希望对你有一定的参考价值。
如何实现数据在多个线程间的可见性
使用测试代码,观察value值生成的汇编代码:
volatile int value = 1;
@Test
public void test()
value++;
反汇编观察value++时候的代码:
0x000000011541f775: lock addl $0x0,(%rsp) ;*putfield queue
addl操作前面,会加入一个lock前缀指令。
下面来查询lock前缀指令的作用:lock
描述翻译:
- lock前缀指令是跟着其他指令的 一个伴随的指令,这个指令会把其他伴随的指令变成一个原子的操作。在多处理器中,lock确保处理器能独占共享变量。
- 其他的,就是一些支持前缀指令的其他指令。
lock前缀指令,在硬件级别的实现,对应的是缓存一致性协议(MESI)。
简单来,对于JMM模型,每个线程都会在自己的工作空间中保留变量副本,例如,当有两个线程t1,t2,拿到内存中同一个共享变量,t1更新了共享变量,lock前缀指令,会通过缓存一致性协议,将数据更新到主内存,同时,t2线程里面缓存的变量副本会收到监听消息,将缓存值置为失效,当t2再次读取自己工作空间中的变量副本,会重新从主内存加载,读取到最新值。
如何实现禁止指令的重排序
JMM是通过编译器在生成字节码时,在指令序列中添加“内存屏障”来禁止指令重排序的。
- JMM内存屏障
LoadLoad屏障 | 对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕 |
StoreStore屏障 | 对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见 |
LoadStore屏障 | 对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕 |
StoreLoad屏障 | 对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见 |
JMM对于volatile修饰的变量,都会加入如下读写内存屏障:
对于读操作:
LoadLoad
volatile 读
LoadLoad
对于写操作:
StoreLoad
volatile 写
StoreLoad
上面是JMM在处理器级别对volatile变量实现禁止指令重排序的操作。
以上是关于volatile实现解析的主要内容,如果未能解决你的问题,请参考以下文章