volatile 关键字

🏳️‍🌈个人网站:code宝藏 👈,欢迎访问
🎉我的公众号:code宝藏 👨‍💻,分享自己的学习资源,欢迎关注🌻
🤝非常感谢大家的支持与点赞👍

有序性问题

为了提高性能,在遵守 as-if-serial 语义(即不管怎么重排序,单线程下程序的执行结果不能被改变。编译器,runtime 和处理器都必须遵守。)的情况下,编译器和处理器常常会对指令做重排序。

一般重排序可以分为如下三种类型:

● 编译器优化重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
● 指令级并行重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
● 内存系统重排序。由于处理器使用缓存和读 / 写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
从 Java 源代码到最终执行的指令序列,会分别经历下面三种重排序:

可见性问题

Java 内存模型抽象了线程和主内存之间的关系,就比如说线程之间的共享变量必须存储在主内存中。Java 内存模型主要目的是为了屏蔽系统和硬件的差异,避免一套代码在不同的平台下产生的效果不一致。

在当前的 Java 内存模型下,线程可以把变量保存本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。
这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。

要解决这个问题,就需要把变量声明为 volatile ,这就指示 JVM,这个变量是共享且不稳定的,每次使用它都到主存中进行读取。

所以,volatile 关键字 除了防止 JVM 的指令重排 ,还有一个重要的作用就是保证变量的可见性。

volatile 提供 happens-before 的保证,同时确保一个线程对共享变量的修改能对其他线程是可见的。当一个共享变量被 volatile 修饰时,它会保证修改的值会立即被更新到内存,当有其他线程需要读取时,它会去内存中读取新值。

原理

volatile 的底层实现原理是内存屏障,Memory Barrier(Memory Fence)

● 对 volatile 变量的写指令后会加入写屏障
● 对 volatile 变量的读指令前会加入读屏障

如何保证可见性

● 写屏障(sfence)保证在该屏障之前的,对共享变量的改动,都同步到主存当中
● 读屏障(lfence)保证在该屏障之后,对共享变量的读取,加载的是主存中最新数据

如何保证有序性

● 写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后
● 读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前

从实践角度而言,volatile 的一个重要作用就是和 CAS 结合,保证了原子性,详细的可以参见 java.util.concurrent.atomic 包下的类,比如 AtomicInteger。
volatile 常用于多线程环境下的单次操作 (单次读或者单次写)。

原子性

volatile不能保证原子性,比如下面这里的读取操作跟写入操作不是原子性的,所以volatile无法保证原子性

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一定会去到彩虹海的麦当

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

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

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

打赏作者

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

抵扣说明:

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

余额充值