volatile内存语义

volatile内存语义、原理详解、内存屏障 volatile 阅读详情

当声明共享变量为volatile后,对这个变量的读/写将会很特别。为了揭开volatile的神秘面纱,下面将介绍volatile的内存语义及其实现。

1.volatile的实现

  理解volatile特性的一个好方法是吧对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。下面通过示例来进行说明。

class VolatileFeaturesExample {
	volatile long v1 = 0L;				//使用volatile声明64位的long型变量
	
	public void set(long l) {
		v1 = l;							//单个volatile变量的写
	}

	public void getAndIncrement() {
		v1++;							//复合(多个)volatile变量的读/写
	}
	
	public long get() {		
		return v1;						//单个volatile变量的读
	}
	
}

假设有多个线程分别调用上面程序的3个方法,这个程序在语义上和下面的程序等价

class VolatileFeaturesExample {
	volatile long v1 = 0L;				//64位的long型普通变量
	
	public synchronized void set(long l) {
		v1 = l;							//对单个普通变量的写用同一个锁同步
	}

	public void getAndIncrement() {		//普通方法调用
		long temp = get();				//调用已同步的读方法
		temp += 1L;					//普通写操作
		set(temp)						//调用已同步的写方法
	}
	
	public synchronized long get() {	//对单个普通变量的读用同一个锁同步		
		return v1;
	}
	
}

  如上面程序所示,一个volatile变量的单个读/写操作,与一个普通变量的读/写操作都是使用同一个锁来同步,他们之间的执行效果相同。
  锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到对这个volatile变量最后的写入。
  锁的语义决定了临界区代码的执行具有原子性。这意味着,即使是64位的long型和double型变量,只要他是volatile变量,对该变量的读/写就具有原子性。如果是多个volatile操作或类似于volatile++这种复合操作,这些操作整体上不具有原子性。
  简而言之,volatile变量自身具有以下特性:
1.可见性。对一个volatile变量的读,总是能看到对这个volatile变量最后的写入。
2.原子性。对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。

2.volatile写-读建立的happens-before关系

  上面讲的是volatile变量自身的特性,对程序员来说,volatile对线程的内存可见性的影响比volatile自身的特性更为重要,也更需要我们关注。
  从JDK1.5开始volatile变量的写-读可以实现线程之间的通信。
  从内存语义的角度来说,volatile变量的写-读与锁的释放-获取有相同的内存效果:volatile写和锁的释放有相同的内存语义,volatile的读与锁的获取有相同的内存语义。

class VolatileExample {
	int a = 0;
	volatile boolean flag = false;

	public void writer() {
		a = 1;				// 1
		flag = true;		// 2
	}

	public void reader() {
		if (flag) {			// 3
			int i = a;		// 4
			...
		}
	}
	
}

  假设线程A执行writer()方法之后,线程B执行reader()方法,根据happens-before规则,这个过程简历的happens-before关系可以分为3类:
1.根据程序次序规则,1 happens-before 2;3 happens-before 4;
2.根据volatile规则,2 happens-before 3;
3.根据happens-before的传递性规则,1 happens-before 4;

3.volatile写-读的内存语义

1.volatile写的内存语义
  当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中。
2.volatile读的内存语义
  当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量。

4.volatile内存语义的实现

  下面来看看JMM如何实现volatile的内存语义
  前面我们提到过重排序分为编译器重排序和处理器重排序。为了实现volatile语义,JMM会分别限制这两种类型得重排序类型。

1.当第二个操作时volatile写时,不管第一个操作是什么,都不能重排序。这个规则确保volatile写之前的操作不会被编译器重排序到写之后。
2.当第一个操作时volatile读时,不管第二个操作是什么,都不能重排序。这个规则确保volatile读之后的操作不会被编译器重排序到读之前。
3.当第一个操作时volatile写,第二个操作是volatile读时,不能重排序。

  为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。对于编译器来说,发现一个最优布置来最小化插入屏障的总数几乎不可能。为此,JMM采取保守策略。下面是基于保守策略的JMM内存屏障插入策略。
1.在每个volatile写操作的前面插入一个StoreStore屏障。
2.在每个volatile写操作的前面插入一个StoreLoad屏障。
3.在每个volatile读操作的前面插入一个LoadLoad屏障。
4.在每个volatile读操作的前面插入一个LoadStore屏障。

5.JSR-133为什么要增强volatile的内存语义

  在JSR-133之前的旧Java内存模型中,虽然不允许volatile变量之间重排序,但旧的Java内存模型允许volatile变量与普通变量重排序。因此,在旧的内存模型中,volatile的写-读没有锁的释放-获取所具有的内存语义。为了提供一种比锁更轻量级的线程之间通信的机制,JSR-133专家组决定增强volatile的内存语义: 严格限制编译器和处理器对volatile变量与普通变量的重排序,确保volatile的写-读与锁的释放-获取具有相同的内存语义。从编译器重排序规则和处理器内存屏障插入策略来看,只要volatile变量与普通变量之间的重排序可能会破坏volatile的内存语义,这种重排序就会被编译器重排序规则和处理器内存屏障插入策略禁止。
  由于volatile仅仅保证对单个volatile变量的读/写具有原子性,而锁的互斥执行的特性可以确保对整个临界区代码的执行具有原子性。在功能上,锁比volatile更强大,在可伸缩性和性能上,volatile更有优势。


并发编程系列之volatile内存语义 前言 前面介绍顺序一致性模型时,我们提到了程序如果正确的同步就会具备顺序一致性,这里所说的同步泛指广义上的同步,其中包括就包括同步原语volatile,那么volatile声明的变量为什么就能保证同步呢?这又是如何实现的呢?今天就让我们一起来揭开这神秘的面纱,OK,开始我们今天的并发编程之旅吧。 volatile的特性 volatile特性可以理解为对volatile变量的单个读写,看成是使用了同一个锁对这些单个读写进行了同步,他们之间的执行效果是一样的;volatile变量自身具有下面2种特性: 阅读详情

相关推荐

思科路由器、交换机组网实验报告

通过思科模拟器搭建路由交换网络

ghj528的博客 1334

理解volatile

特性 可见性:对一个volatile变量的读,总能看到任意线程对这个volatile变量最后的写。 原子性:对任意单个volatile变量的读/写具有原子性。 如何实现的? 对volatile的写操作其汇编代码会多出一步带lock前缀的指令: 0x01a3de24: **lock** addl $0x0,(%esp); 该指令在多核处理器下引发两件事情: 1,将当前处理器缓存行的数据写回到系统内...

业精于勤荒于嬉 行成于思毁于随 358

双足步行机器人的ZMP与CoP检测

静态步行与动态步行   机器人步态分为静态步行和动态步行。当机器人做静态步行运动时,身体的各个部分运动速度很小,机器人的整体稳定性较易控制。静态步行稳定性采用机器人的重心地面投影点(Center of Gravity,简称CoG)作为稳定性标准,这种判定方法适用于运动较为缓慢的情况。如果机器人采用的是一种高速运动的步行方式,则称之为动态步行。当机器人进行动态步行时,其重心的位置和加速度的大小时...

weixin_34112208的博客 5906

java volatile语义

volatile是用来保证可见性的。 线程在执行过程中,所有的数据都保留在自己的本地内存中,各个线程的本地内存相互独立,互不影响。一般直到线程执行完毕后再写到主存。这样线程操作共享变量时,会有存在数据不一致问题,volatile就是为了让共享变量及时更新到主存中。 volatile 不具有原子性,只是保证前一秒取出来的数据是最新的,但是对于后一秒别的线程修改了数据是没有办法的,对于count++...

Forget_G的博客 200

手把手教你用差分放大电路搭建LC振荡器(实测频率误差分析)

本文通过一个差分放大电路搭建LC振荡器的实践案例,深入分析了实测频率与理论值产生显著误差的原因。核心在于共模电感的互感效应改变了谐振回路的等效电感,导致频率偏差。文章提供了从电路搭建、误差根源剖析到波形优化与稳定性提升的完整解决方案,帮助电子爱好者深入理解模拟电路的实际行为。

svm4gardener的博客 541

使用PaddleOCR识别查验平台验证码

PaddleOCR识别国税查验平台验证码

m0_63932300的博客 1266

.JSR-133对volatile内存语义的增强

.JSR-133对volatile内存语义的增强

小汤圆 279

学Simulink——基于Simulink的Buck-Boost变换器双闭环PI+抗饱和设计建模示例

本文详细介绍了基于Simulink的非反相Buck-Boost变换器双闭环控制设计方法。针对Buck-Boost变换器特有的右半平面零点(RHPZ)和控制挑战,提出了电压-电流双闭环PI控制架构,并集成抗积分饱和机制。通过Simulink建模,实现了主电路、电流内环PI、电压外环PI、PWM及占空比限幅等模块的搭建,验证了在输入电压跌落(20V→10V)和负载突变(2A→4A)等场景下的稳定性能。仿真结果表明,输出电压波动小于0.35V,恢复时间快于1ms,电感电流有效限幅。文章还讨论了工程实践要点和扩展方

xiaoheshang_123的博客 318

volatile的内存语义

前言 当声明共享变量为volatile后,对这个变量的读/写将会很特别。为了揭开volatile的神秘面 纱,下面将介绍volatile的内存语义及volatile内存语义的实现。 volatile的应用与底层原理详见:《volatile的应用与底层原理》 volatile的特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,看成是使用同一个锁对这些单个读/写操作做了同步。下面通过具体的示例来说明,示例代码如下。 class VolatileFeaturesExampl

Daryl的博客 504

2026全网最新华为OD机试真题目录A+B+C+D+E卷(持续收录中)-Python版

🔔本文收录于「最新最全华为OD机试真题(Python版)」专栏,手把手带你零基础教学华为OD机试。本题集提供最优题解思路,解题步骤,代码解析,复杂度分析及最优题解源码等,支持多语言题解,助你轻松拿捏OD机考,一举上岸!安利大家关注&&收藏&&订阅!题库正在疯狂收录中,up!up!up!! 🚫提醒:拒绝一切代考/替考,违法必究!本人所写题库均精心搜集于互联网。    💗订阅福利:一次订阅,可永久免费阅读,提供在线答疑解惑,后续题库更新皆可阅读使用!

**My Coding Family** 10万+

【33】软考软件设计师——算法题(二):贪心法与回溯法深度精讲|第4题进阶满分攻略

本文是《软件设计师·50讲通关|从零基础到工程师职称》专栏第33篇,承接第32篇分治法与动态规划,聚焦**下午第4题算法大题进阶核心**,详解贪心法与回溯法两大高频难点算法。全文超4000字深度拆解贪心法核心原理、贪心选择性质、局部最优推全局最优的底层逻辑,厘清贪心算法适用边界与经典误区;深挖回溯法深度优先遍历思想、递归回溯三步流程、可行性剪枝与最优性剪枝两大核心技巧;全覆盖活动选择、零钱兑换、01背包贪心禁区、八皇后、全排列等软考必考经典题型;配套标准化算法框架代码、考场填空模板、剪枝优化实操案例

专注于人工智能、软件开发、工控自动化、工厂数字化及智能化等领域,希望和大家共同进步! 438

volatile语义级别分析

https://www.cnblogs.com/keeya/p/9255136.html 1。可见行 2、对变量的读写操作保证原子性,但是不能保证i++这类的操作因为这是三个操作也叫复合操作,这里里面包括了读取值 修改,写回到主内存。可能在前面读的时候拿到了最新的,但是可能其他线程在这个线程修改操作前,进行了修改然后相当于两个线程进行了相同的操作。 volatile读写建立的的happeds-before关系: java5也就是JSR-133以后volatile修饰的可以进行线程间通信,...

weixin_42546729的博客 269

21天学会基于 Linux 的 NPU 固件开发--2.2 固件加载流程:request_firmware() 深度解析

本文深度解析Linux内核中NPU固件加载流程,重点剖析request_firmware()机制。内容涵盖:1)内核态与用户态的交互机制,包括同步/异步加载方式;2)NPU固件的特殊格式与加载逻辑;3)调试技巧与安全验证方案;4)提供最小化固件加载器实验。文章揭示了从文件系统到硬件初始化的完整链路,探讨了大模型时代面临的性能挑战与优化方案,如零拷贝加载技术,并提出了版本兼容性、断电恢复等关键问题的思考方向。

amy_mhd的博客 59

Java并发(四):volatile的内存语义

volatile可见性实验volatile的特性volatile 写与读的内存语义写语义读语义总结一下读写语义volatile的实现volatile的内存语义实现 前面我们已经讲过了volatile的作用、底层实现与内存屏障 下面就总结一下整个流程 volatile可见性实验 举个栗子 我这里开了两个线程,后面的线程去修改volatile变量,前面的线程不断获取volatile变量, 结果是会一致卡在死循环,控制台没有任何输出 假如将flag让volatile来进行修饰 结果是:三秒后,就不会不断打印.

GDUT_Trim的博客 1829

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

volatile 内存语义

volatile 内存语义 1. volatile 的特性 volatile变量的单个读/写,可以看成是使用同一个锁对这些单个读/写操作做了同步 volatile long val = 0L; // 使用volatile声明64位的long型变量 public void set(long l) { val = l; // 单个volatile变量的写 } public void getAn...

谷家舟 207

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

volatile内存语义的实现

为了实现volatile的内存语义,编译器在生成字节码时,会把指令序列中插入内存屏障来禁止特定类型的处理器重排序。下面是基于保守策略的JMM内存屏障插入策略 在每个volatile写操作前面插入一个StoreStore屏障 在每个Volatile写操作后面插进入一个StoreLoad屏障 在每个volatile读操作的后面插入一个LoadLoad屏障 在每个volatile读操作的后面插入LoadStore屏障 ...

大龄毕业生的博客 268

volatile关键字的内存语义

volatile关键字的理解 当我们声明一个变量为 volatile 时,程序对于该变量的读和写就会变得十分特别,具体的特别之处,我们还是要看看 volatile 关键字的内存语义,观察其特别之处。 下面是一个简单的例子,通过这个例子我们来具体说明 volatile 关键字的内存语义 class VolatileFeatures { volatile long value = 0L; pulbic void set(long val) { value = val; } public void

sulayman的博客 202

揭开volatile的神秘面纱——熟悉volatile 的内存语义

当声明共享变量为 volatile 后,对这个变量的读/写将会很特别。为了揭开 volatile 的神秘面纱,本文将介绍 volatile 的内存语义及 volatile 内存语义的实现。

秃了也弱了 455

volatile的内存语义与应用

volatile的内存语义 volatile的特性 理解volatile特性的一个好方法是把对volatile变量的单个读/写,堪称是使用同一个锁对这些单个读/写操作做了同步。 锁的happens-before规则保证释放锁和获取锁的两个线程之间的内存可见性,这意味着对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。 锁的语义决定了临界区代码的执...

163

Java面试-volatile的内存语义

1、volatile的特性 理解volatile特性的一个好办法是把对volatile变量的单个读/写,看成是使用同一个锁对单个读/写操作做了同步。 ​ 代码示例: package com.lizba.p1; /** * <p> * volatile示例 * </p> * * @Author: Liziba * @Date: 2021/6/9 21:34 */ public class VolatileFeatureExample { /** 使用

Java框架、并发编程、分布式、微服务、Redis、HarmonyOS、中间件等技术 625
上一篇: Java并发机制的底层实现原理
下一篇: 锁的内存语义
geek-wmy
博客等级 码龄9年 322粉丝 126原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值