volatile

作者: 7d972d5e05e8 | 来源:发表于2020-06-28 23:25 被阅读0次

    一、volatile可见性和禁止重排序是怎么实现的?

    首先从JMM内存模型层面,volatile通过storestore,storeload,loadload,loadstore四个定义好的内存屏障来实现的。

    原文链接:https://blog.csdn.net/Xiewanru/article/details/97647184

    volatile通过插入读屏障和写屏障保证可见性,在volatile禁止重排序上,也是通过内存屏障实现的。
    因为内存屏障可以使一些指令按照特定顺序执行。
    volatile禁止指令重排序的规则:
     1.当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
     2.当地一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
     3.当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

    用人话就是下面的解释:
    下面是基于保守策略的JMM内存屏障插入策略:
    在每个volatile写操作的前面插入一个StoreStore屏障。
    在每个volatile写操作的后面插入一个StoreLoad屏障。
    在每个volatile读操作的前面插入一个LoadLoad屏障。
    在每个volatile读操作的后面插入一个LoadStore屏障。

    内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。

    内存屏障可以被分为以下几种类型

    1. LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
    2. StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
    3. LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
    4. StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

    参考文章:
    Java并发:volatile内存可见性和指令重排
    volatile 和 内存屏障

    里面都提到了storeload这种JMM内存屏障,但是通过查看字节码发现volatile和普通变量生成的字节码没有任何区别。那么这些区别提现在哪里呢?我们就去看下它的汇编代码,发现指令多了lock修饰。

    参考文章:
    从汇编看Volatile的内存屏障
    这篇文章也和我们的验证是一样的,用cpu指令lock来实现内存屏障。

    查询IA32手册,它的作用是使得本CPU的Cache写入了内存,该写入动作也会引起别的CPU invalidate其Cache。所以通过这样一个空操作,可让前面volatile变量的修改对其他CPU立即可见。

    所以,它的作用是
    锁住主存:

    1. 任何读必须在写完成之后再执行
    2. 使其它线程这个值的栈缓存失效
    3. 类似于前面是storestore,后面是storeload

    总结:个人理解,JMM语义定义了storeload等4个内存屏障,然后真正的实现是由cpu指令lock去模拟实现的,用来达到store load效果。

    相关文章

      网友评论

          本文标题:volatile

          本文链接:https://www.haomeiwen.com/subject/pnagfktx.html