当前位置:网站首页>volatile关键字及其作用
volatile关键字及其作用
2022-07-27 01:32:00 【tea-Sir】
本文主要介绍Java语言中的volatile关键字,内容涵盖volatile的保证内存可见性、禁止指令重排等。
1 保证内存可见性
1.1 基本概念
可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能看到。
1.2 实现原理
当对非volatile变量进行读写的时候,每个线程先从主内存拷贝变量到CPU缓存中,如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的CPU cache中。
volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,保证了每次读写变量都从主内存中读,跳过CPU cache这一步。当一个线程修改了这个变量的值,新值对于其他线程是立即得知的。
2 禁止指令重排
2.1 基本概念
指令重排序是JVM为了优化指令、提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。指令重排序包括编译器重排序和运行时重排序。
latile变量禁止指令重排序。针对volatile修饰的变量,在读写操作指令前后会插入内存屏障,指令重排序时不能把后面的指令重排序到内存屏
示例说明:
double r = 2.1; //(1)
double pi = 3.14;//(2)
double area = pi*r*r;//(3)
虽然代码语句的定义顺序为1->2->3,但是计算顺序1->2->3与2->1->3对结果并无影响,所以编译时和运行时可以根据需要对1、2语句进行重排序。
2.2 指令重排带来的问题
如果一个操作不是原子的,就会给JVM留下重排的机会。
线程A中
{
context = loadContext();
inited = true;
}
线程B中
{
if (inited)
fun(context);
}
如果线程A中的指令发生了重排序,那么B中很可能就会拿到一个尚未初始化或尚未初始化完成的context,从而引发程序错误。
2.3 禁止指令重排的原理
volatile关键字提供内存屏障的方式来防止指令被重排,编译器在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
JVM内存屏障插入策略:
每个volatile写操作的前面插入一个StoreStore屏障;
在每个volatile写操作的后面插入一个StoreLoad屏障;
在每个volatile读操作的后面插入一个LoadLoad屏障;
在每个volatile读操作的后面插入一个LoadStore屏障。
2.4 指令重排在双重锁定单例模式中的影响
基于双重检验的单例模式(懒汉型)
public class Singleton3 {
private static Singleton3 instance = null;
private Singleton3() {}
public static Singleton3 getInstance() {
if (instance == null) {
synchronized(Singleton3.class) {
if (instance == null)
instance = new Singleton3();// 非原子操作
}
}
return instance;
}
}
instance= new Singleton()并不是一个原子操作,其实际上可以抽象为下面几条JVM指令:
memory =allocate(); //1:分配对象的内存空间
ctorInstance(memory); //2:初始化对象
instance =memory; //3:设置instance指向刚分配的内存地址
上面操作2依赖于操作1,但是操作3并不依赖于操作2。所以JVM是可以针对它们进行指令的优化重排序的,经过重排序后如下:
memory =allocate(); //1:分配对象的内存空间
instance =memory; //3:instance指向刚分配的内存地址,此时对象还未初始化
ctorInstance(memory); //2:初始化对象
指令重排之后,instance指向分配好的内存放在了前面,而这段内存的初始化被排在了后面。在线程A执行这段赋值语句,在初始化分配对象之前就已经将其赋值给instance引用,恰好另一个线程进入方法判断instance引用不为null,然后就将其返回使用,导致出错。
解决办法
用volatile关键字修饰instance变量,使得instance在读、写操作前后都会插入内存屏障,避免重排序。
public class Singleton3 {
private static volatile Singleton3 instance = null;
private Singleton3() {}
public static Singleton3 getInstance() {
if (instance == null) {
synchronized(Singleton3.class) {
if (instance == null)
instance = new Singleton3();
}
}
return instance;
}
}
3 volatile无法保证原子性
由于num++非原子操作,先获取num的值,然后写回新的num,最后新值num++;
A线程和B线程可能同时取了一个相同的值,导致线程不安全。
解决方法:给方法增加一个synchronized关键字,但加synchronized就不需要volatile了。
/** * @description:volatile非原子性测试 * @author: teasir * @create: 2022/07/22 **/
public class VolatileVisibility {
private volatile int num;
public int inscrease(){
return num++;
}
}
4 适用场景
(1)volatile是轻量级同步机制。在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,是一种比synchronized关键字更轻量级的同步机制。
(2)volatile无法同时保证内存可见性和原子性。加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性。
(3)volatile不能修饰写入操作依赖当前值的变量。声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子操作:“count++”、“count = count+1”。
(4)当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile;
(5)volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
边栏推荐
- {“errcode“:44001,“errmsg“:“empty media data, hint: [1655962096234893527769663], from ip: 222.72.xxx.
- 优炫数据库集群如何唯一标识一条用户SQL
- 1.28亿美元!芬兰量子计算公司IQM获世界基金支持
- [dynamic planning medium] leetcode 198. looting 740. delete and get points
- Hcip day 14 notes
- Analysis of [paper] pointlanenet papers
- 易灵思T35 FPGA驱动LVDS显示屏
- Common events of window objects
- Skywalking系列学习之告警通知源码分析
- “满五唯一”和“满二唯一”是什么?有什么不同?
猜你喜欢

Use the most primitive method to manually implement the common 20 array methods
![[learning notes, dog learning C] string + memory function](/img/53/86e529dcc8a5a6b682e9485e7c152a.png)
[learning notes, dog learning C] string + memory function

spark学习笔记(五)——sparkcore核心编程-RDD转换算子

Make ppt timeline

Baidu cloud face recognition

Hcip day 14 notes
![[paper]PointLaneNet论文浅析](/img/f6/8001be4f90fe15100e0295de02491f.png)
[paper]PointLaneNet论文浅析

客户案例 | 关注老年用户体验,银行APP适老化改造要避虚就实
The most complete basic knowledge of software testing in the whole network (a must for beginners)

图解 SQL,这也太形象了吧!
随机推荐
shell awk
[SQL simple question] leetcode 627. change gender
太强了,一个注解搞定接口返回数据脱敏
spark:计算不同分区中相同key的平均值(入门级-简单实现)
【flask】服务端获取客户端请求的文件
Abbkine AbFluor 488 细胞凋亡检测试剂盒特点及实验建议
pip3 设置阿里云
Hcip day 14 notes
Worthington果胶酶的特性及测定方案
vector 转 svg 方法
1.28亿美元!芬兰量子计算公司IQM获世界基金支持
win10/win11无损扩大C盘空间,跨盘合并C、E盘
Complete source code of mall applet project (wechat applet)
队列达到最大长度代码实战
Shell (38): SSH port forwarding
Worthington过氧化物酶活性的6种测定方法
185. 部门工资前三高的所有员工(必会)
力扣(LeetCode)207. 课程表(2022.07.26)
$128million! IQM, a Finnish quantum computing company, was supported by the world fund
数据湖(二十):Flink兼容Iceberg目前不足和Iceberg与Hudi对比