22.Volatile原理

Volatile原理

1.Volatile语义中的内存屏障

在Java代码中,volatile关键字主要又两层语义

  1. 不同线程对volatile变量的值具有内存可见性,就是一个线程修改了某个volatile变量的值,该值对其他线程立即可见。
  2. 禁止指令重排序

同时volatile关键字不仅能保证可见性,还能保证有序性,保证有序性是通过内存屏障指令来确保的。

JVM编译器会在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的CPU重排序。

JVM在处理volatile关键字修饰的变量时,会采取保守策略来确保内存可见性和有序性,这涉及到内存屏障(Memory Barrier)的使用。内存屏障是一种硬件层面的指令,用于确保某些内存访问操作的执行顺序,防止CPU的乱序执行对并发程序的正确性产生影响。对于volatile变量的读写,JVM会分别在读和写操作前后插入适当类型的内存屏障,确保以下几点:

  1. 全局可见性: 保证对volatile变量的写操作能立即对其他线程可见,即使在不同的CPU缓存中也是如此。这意味着写操作之后,修改的值会立刻刷出到主内存中。
  2. 禁止重排序: 防止编译器和CPU对涉及volatile变量的代码进行不必要的重排序,确保它们按照程序员指定的顺序执行。这对于依赖于特定顺序的并发控制逻辑至关重要。

基于保守策略的volatile操作内存屏障插入策略主要包括以下方面:

  • 写屏障(Store Memory Barrier): 在写入volatile变量之后插入。它的作用是确保在该屏障之前的所有普通写操作(非volatile)都已完成,并且将当前线程的工作内存中的volatile变量值刷新到主内存中。这样,任何读取该volatile变量的线程都能看到最新值。
    • 在每个volatile操作面插入一个StoreStore屏障
    • 在每个volatile操作面插入一个 StoreLoad屏障
  • 读屏障(Load Memory Barrier): 在读取volatile变量之前插入。它的作用是确保读取操作之后的加载不会被重排序到该屏障之前,并且使CPU读取主内存中的最新值,而不是使用缓存中的旧值,从而确保读取到的是最近一次写入的值,无论这个写入操作发生在哪个线程中。
    • 在每个volatile操作面插入一个LoadLoad屏障
    • 在每个volatile操作面插入一个 LoadStore屏障

这些屏障的联合使用确保了对volatile变量的读写操作具有原子性和全局有序性,尽管它们不保证复合操作(如count++)的原子性。这就是为什么即使在没有锁的情况下,volatile也能作为轻量级的同步机制,用于状态标记、双重检查锁定模式等场景。

1.1.volatile写操作的内存屏障

volatile写操作的内存屏障插入策略为:在每个volatile写操作前插入SotreStore(SS)屏障,在写操作之后加上StoreLoad屏障

1.1.1.StoreStore 屏障

定义与作用StoreStore屏障主要用于确保一个存储(写)操作在另一个存储操作之前完成。换句话说,它强制所有在该屏障之前的存储操作在该屏障之后的存储操作之前完成。这种屏障通常用于避免写-写冲突导致的数据不一致问题,尤其是在处理器有乱序执行能力的体系结构中。

  • 前面的写入不会重排序到后面
  • 前面的写指令完成后,高速缓存数据刷入主存
  • 后面的写操作不会排序到前面

应用场景: 例如,在实现某些类型的锁释放操作时,可能需要确保解锁操作前的所有写操作已经完成,以免新获得锁的线程看到不一致的状态。

1.1.2.StoreLoad 屏障

定义与作用StoreLoad屏障是Java内存模型中最强大的一种屏障,它确保在屏障之前的所有写操作(存储操作)在屏障之后的任何读操作(加载操作)之前完成。这意味着不仅要求写操作完成,而且要确保这些写操作对所有线程可见。因此,StoreLoad屏障通常用于实现volatile

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值