美文网首页
volatile关键字-内存屏障底层原理

volatile关键字-内存屏障底层原理

作者: 盼旺 | 来源:发表于2023-04-18 17:51 被阅读0次

    volatile关键字修饰变量的特点

    保证了并发编程中变量的可见性和有序性。

    为啥普通变量不可见?

    Java内存模型中规定所有的变量都存储在主内存中,每条线程都有自己的工作内存,线程工作时其实是把变量从主内存从copy一份到工作内存。
    对于变量的读、写都必须在工作内存中进行,而不能直接读、写主内存中的变量,所以工作内存的变量也无法被其他线程直接访问。


    那在工作内存对变量修改了什么时候同步到主内存?

    这个同步的时间点是由Java内存模型来控制的。但是有个规则叫happens-before:制定了一些操作之间的顺序关系,比如:
    程序顺序规则:在一个线程内,按照程序代码的顺序,前面的操作“happens-before”后面的操作。
    锁定规则:一个unlock操作“happens-before”后续的lock操作,要释放锁。
    volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作“happens-before”后续的读操作。
    传递性规则(Transitivity Rule):如果操作A“happens-before”操作B,操作B“happens-before”操作C,那么操作A“happens-before”操作C。
    线程启动规则(Thread Start Rule):一个线程的启动操作“happens-before”该线程的任何操作。
    线程终止规则(Thread Termination Rule):一个线程的任何操作“happens-before”该线程的终止操作。
    线程中断规则(Thread Interruption Rule):对一个线程的中断操作“happens-before”该线程的任何操作。
    对象终结规则(Finalizer Rule):一个对象的初始化操作“happens-before”该对象的finalize()方法的开始执行。
    这其中就有我们的主角volatile

    那volatile做了什么保证可见性

    当对volatile变量执行写操作后,Java内存模型会把工作内存中的最新变量值强制刷新到主内存(内存屏障中的写屏障)
    写操作会导致其他线程中的缓存无效
    这样,其他线程使用缓存时,发现本地工作内存中此变量无效,便从主内存中获取,这样获取到的变量便是最新的值,实现了线程的可见性。

    对于volatile保证有序性(禁止指令重排序)的理解

    原理就是内存屏障

    内存屏障有两个效果:

    阻止指令重排序:在插入内存屏障指令后,不管前面与后面任何指令,都不能与内存屏障指令进行重排(这个时候需要理解为啥多线程下一个变量的赋值会出现指令重排),保证前后的指令按顺序执行,即保证了顺序性。
    全局可见:插入的内存屏障,保证了其对内存操作的读写结果会立即写入内存,并对其他CPU核可见,即保证了可见性,解决了普通读写的延迟问题。例如,插入读屏障后,能够删除缓存,后续的读能够立刻读到内存中最新数据。插入写屏障后,能够立刻将缓存中的数据刷新入内存中,使其对其他CPU核可见

    因此,在CPU的物理世界里,内存屏障通常有三种:

    lfence: 读屏障(load fence),即立刻让工作内存失效,从主内存中读取数据,并装载入工作内存中。
    sfence: 写屏障(write fence), 即立刻进行flush,把缓存中的数据刷入主内存中。
    mfence: 全屏障 (memory fence),即读写屏障,保证读写都串行化,确保数据都写入内存并清除缓存。

    为啥会出现有指令重排这个机制?编译器和处理器不是脑子瓦特了?

    CPU的执行速度远远快于内存的读写速度
    如果一条指令需要等待某个操作完成才能继续执行,那么CPU可以在等待的同时执行其他指令,从而提高CPU的利用率和执行效率。

    为啥多线程下一个变量的赋值会出现指令重排

    instance = new Singleton()这句,这并非是一个原子操作,事实上
    在 JVM 中这句话大概做了下面 3 件事情。
    1.给 instance 分配内存
    2.调用 Singleton 的构造函数来初始化成员变量
    3.将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)
    但是在 JVM 的即时编译器中存在指令重排序的优化。也就是说上面的第二步和第三步的顺序是不能保证的,最终的执行顺序可 能是 1-2-3 也可能是 1-3-2。如果是后者,则在 3 执行完毕、2 未执行之前,被线程二抢占了,这时 instance 已经是非 null 了(但却没有初始化),所以线程二会直接返回 instance,然后使用,然后顺理成章地报错

    Volatile为啥不具有原子性

    比如一个Volatile变量i操作i++
    因为你从主内存读取到Volatile修饰的关键字后去修改,你没有锁住它,别人也可以动的,那么你的修改就会被覆盖了,所以不能保证结果的正确性。

    相关文章

      网友评论

          本文标题:volatile关键字-内存屏障底层原理

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