volatile的内存语义
但凡了解过并发编程的程序猿,应该都知道volatile解决了并发编程的可见性和有序性问题,但底层具体是如何实现的呢?
有序性
volatile可以禁止指令重排序,从而保证并发编程的有序性。那volatile如何实现禁止指令重排序的呢?答案就是内存屏障。
内存屏障是一类同步屏障指令,是CPU或者编译器在对内存进行随机访问操作中的一个同步点,使得此点之前的所有读写操作都执行之后才开始执行此点之后的剩余操作。
下表详细描述了和volatile有关的指令重排序行为:
能否重排序 | 第二个操作 | ||
---|---|---|---|
第一个操作 | 普通读写 | volatile读 | volatile写 |
普通读/写 | no | ||
volatile读 | no | no | no |
volatile写 | no | no |
由上表可以得出以下结论:
- 当第二个操作是volatile写时,前一个操作无论是什么,都不能进行重排序。这个规则确保volatile写之前的操作不会被编译器重排序到volatile写之后;
- 第一个操作为volatile读时,不管第二个操作是什么,都不能进行重排序。这个规则确保volatile读之后的操作不会被编译器重排序到volatile读之前。
- 第一个操作为volatile写,第二个操作为volatile读时,不能进行重排序。
这些规则具体的实现方式是通过在编译期生成字节码时,在指令序列中增加内存屏障来保证的,下面是基于保守策略的JMM内存屏障插入策略:
- 在每个volatile写操作的前面插入一个
storestore
屏障- 对于这样的语句:
store1;storesore;store2
,在store2
及后续写入操作执行前,保证store1
的写入结果对其他处理器可见;
- 对于这样的语句:
- 每个volatile写操作的后面加入一个
storeload
屏障- 对于这样的语句
store1;storeload;load2
,在load2
及后续的读取操作执行前,保证store1
的写入的结果对所有处理器可见
- 对于这样的语句
- 每个volatile读操作的后面插入一个
loadload
屏障- 对于这样的语句:
load1;loadload;load2
,在load2
及后续读取操作要读取的数据被访问前,保证load1
要读取的数据被读取完毕
- 对于这样的语句:
- 每个volatile读操作的后面插入一个
loadstore
屏障- 对于这样的语句:
load1;loadstore;store2
,在store2
及后续写入操作被刷出前,保证load1
要读取的数据被读取完毕
- 对于这样的语句:
内存屏障 | 第二个操作 | |||
---|---|---|---|---|
第一个操作 | 普通读 | 普通写 | volatile读 | volatile写 |
普通读 | loadstore | |||
普通写 | storestore | |||
volatile读 | loadload | loadstore | loadload | loadstore |
volatile写 | storeload | storestore |
所以,volatile通过在volatile变量的操作前后插入内存屏障的方式,来禁止指令重排,进而保证多线程情况下对共享变量的有序性。
可见性
volatile对于可见性的实现,内存屏障也起着至关重要的作用。因为内存屏障相当于一个数据同步点,他要保证在这个同步点之后的读写操作必须在这个点之前的读写操作都执行完之后才可以执行。,并且执行器在遇到内存屏障的时候,缓存数据会和主存进行同步,或者是把缓存数据写入主存、或者从主存把数据读取到缓存。
思考题
已经有了缓存一致性协议,为什么还要有volatile呢?
1、并不是所有的硬件架构都提供了相同的一致性保证,Java作为一门跨平台语言,JVM需要提供一个统一的语义。
2、操作系统中的缓存和JVM中线程的本地内存并不是一回事,通常我们可以认为:MESI可以解决缓存层面的可见性问题。使用volatile关键字,可以解决JVM层面的可见性问题。
3、缓存可见性问题的延伸:由于传统的MESI协议的执行成本比较大。所以CPU通过Store Buffer和Invalidate Queue组件来解决,但是由于这两个组件的引入,也导致缓存和主存之间的通信并不是实时的。也就是说,缓存一致性模型只能保证缓存变更可以保证其他缓存也跟着改变,但是不能保证立刻、马上执行。
所以,内存屏障也是保证可见性的重要手段,操作系统通过内存屏障保证缓存间的可见性,JVM通过给volatile变量加入内存屏障保证线程之间的可见性。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南