volatile 核心原理


volatile 在内存语义上有两个作用,一个作用是保证被 volatile 修饰的共享变量对每个线程都是可见的,当一个线程修改了被 volatile 修饰的共享变量之后,另一个线程能够立刻看到修改后的数据。另一个作用是禁止指令重排。

保证可见性原理

volatile 能够保证共享变量的可见性。
如果一个共享变量使用 volatile 修饰,则该共享变量所在的缓存行会被会被要求进行缓存一致性校验。当一个线程修改了 volatile 修饰的共享变量之后,修改后的共享变量的值会立刻刷新到主内存,其他线程每次都从主内存中读取 volatile 修饰的共享变量,这就保证了使用 volatile 修饰的共享变量对线程的可见性。
例如:在程序中使用 volatile 修饰了一个共享变量 count,如下所示:

volatile long count = 0;

此时,线程对这个变量的读写都必须经过主内存。volatile 保证可见性的原理如图:
image.png

保证有序性核心原理

volatile 能够禁止指令重排,从而能够避免在高并发的环境下多个线程之间出现乱序执行的情况。volatile 禁止指令重排是通过内存屏障实现的,内存屏障本质上就是一条 CPU 指令,这个 CPU 指令有两个作用,一个是保证共享变量的可见性,另一个是保证指令的执行顺序。volatile 禁止指令重排的规则如表:

是否可以重排序第二个操作
第一个操作普通读或写volatile 读volatile 写
普通写或读可以重排序可以重排序不能重排序
volatile 读不能重排序不能重排序不能重排序
volatile 写可以重排序不能重排序不能重排序

为了实现上图的禁止指令重排的规则,JVM 编译器可以通过在程序编译生成的指令序列中插入「内存屏障」来禁止在内存屏障前后的指令发生重排。Java 内存模型建议 JVM 采用保守的策略严格禁止指令重排,volatile 读策略如图(1)所示:

  1. 在每个 volatile 读操作的后面都加入一个 LoadLoad 屏障,禁止后面的普通读与前面的 volatile 读发生指令重排。
  2. 在每个 volatile 读操作的后面都加入一个 LoadStore 屏障,禁止后面的普通写与前面的 volatile 读发生指令重排。

image.png
volatile 写策略如图(2)所示:

  1. 在每个 volatile 写操作的前面都加入一个 StoreStore 屏障,禁止前面的普通写与后面的 volatile 写发生指令重排。
  2. 在每个 volatile 写操作的后面都加入一个 StoreLoad 屏障,禁止前面的 volatile 写与后面的 volatile 读发生指令重排。

image.png
这种保守的内存屏障可以保证在任意 CPU 中都能够得到正确的执行结果。

Volatile 的局限性

volatile 虽然能够保证数据的可见性和有序性,但是无法保证数据的原子性。例如,下列代码中同时有两个线程对 volatile 修饰的 Long 类型的 count 值进行累加操作,count 的初始值为 0 ,每个线程都对 count 的值累加 1000 次,代码如下。

public class VolatileAtomicityTest {

    private volatile Long count = 0L;

    public void  incrementCount(){
        count++;
    }

    public Long execute() throws InterruptedException {
        Thread thread1 = new Thread(()->{
            IntStream.range(0, 1000).forEach((i) -> incrementCount());
        });

        Thread thread2 = new Thread(()->{
            IntStream.range(0, 1000).forEach((i) -> incrementCount());
        });

        //启动线程1和线程2
        thread1.start();
        thread2.start();

        //等待线程1和线程2执行完毕
        thread1.join();
        thread2.join();

        //返回count的值
        return count;
    }

    public static void main(String[] args) throws InterruptedException {
        VolatileAtomicityTest multiThreadAtomicity = new VolatileAtomicityTest();
        Long count = multiThreadAtomicity.execute();
        System.out.println(count);
    }
}

运行结果参考如下:
1448
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SuZhan7710

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值