深入理解volatile底层原理

volatile关键字在Java多线程中用于保证共享变量的可见性和有序性,防止指令重排序。它通过内存屏障确保缓存一致性,并在多核处理器下避免数据不一致。然而,volatile并不保证原子性,例如i++这类操作在多线程环境中仍可能出现问题。双重检查锁使用volatile防止对象半初始化,确保线程安全。


volatile 是轻量级的 synchronized,一般作用于变量。相比于synchronized关键字,volatile关键字的执行成本更低,因为它不会引起线程上下文的切换和调度。

volatile主要有以下两个功能

  • 保证共享变量的内存可见性(即当一个线程修改一个共享变量时,另一个线程能读到这个修改的值)
  • 禁止指令重排序

volatile 的用途

从volatile的内存语义上来看,volatile可以保证内存可见性且禁止重排序。

在保证内存可见性这一点上,volatile有着与锁相同的内存语义,所以可以作为一个“轻量级”的锁来使用。但由于volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁可以保证整个临界区代码的执行具有原子性。所以在功能上,锁比volatile更强大;在性能上,volatile更有优势


volatile的特性

volatile可以保证可见性和有序性

  • 可见性:volatile可以保证不同线程对共享变量进行操作时的可见性。即当一个线程修改了共享变量时,另一个线程可以读取到共享变量被修改后的值。

  • 有序性:volatile会通过禁止指令重排序进而保证有序性。

  • 原子性:对于单个的volatile修饰的变量的读/写是可以保证原子性的,但对于v++这种复合操作并不能保证原子性。所以说volatile不具备原子性。

    为什么说 volatile 不保证原子性?

    Java中只有对基本类型变量的赋值和读取是原子操作,如 i = 1的赋值操作,但是像 j = i 或者 i++ 这样的操作都不是原子操作,因为他们都进行了多次原子操作,比如先读取 i 的值,再将 i 的值赋值给 j,两个原子操作加起来就不是原子操作了。

    比如 i++ ,i = i + 1 分为三个操作

    • 读取 i 的值
    • 自增 i 的值
    • 把 i 的值写回内存

    这个过程中可能线程A读取了 i 的值,然后线程切换,即使是被volatile修饰,主存中变量的值也还没变化,所以另一个线程B也读取了 i 未被修改的值,之后线程切换回A,进行 + 1 操作,写回内存;而线程B写回内存会把线程A修改的值覆盖。

    所以即便是volatile具有可见性,也不能保证对它修饰的变量具有原子性。

    可以通过 synchronized和Lock实现原子性。因为synchronized和Lock能够保证任一时刻只有一个线程访问该代码块。


volatile实现内存可见性原理

volatile可以保证内存可见性的关键是volatile的读/写实现了缓存一致性。

缓存一致性协议 MESI 的主要内容为:

多个CPU从主内存读取同一个数据到各自的高速缓存,当其中某个CPU修改了缓存里的数据,该数据会马上同步会主内存,其他CPU通过总线嗅探机制可以感知到数据的变化从而将自己缓存里

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值