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 读
LoadStore

对于写操作:

StoreStore
volatile 写
StoreLoad

上面是JMM在处理器级别对volatile变量实现禁止指令重排序的操作。

以上是关于volatile实现解析的主要内容,如果未能解决你的问题,请参考以下文章

内存屏障和volatile内存语义的实现

volatile如何保证可见性和有序性的?

volatile原理(学习笔记)

Java——聊聊JUC中的volatile与内存屏障

Java——聊聊JUC中的volatile与内存屏障

Java——聊聊JUC中的volatile与内存屏障