22.Volatile原理
文章目录
- Volatile原理
- 1.Volatile语义中的内存屏障
- 1.1.volatile写操作的内存屏障
- 1.1.1.StoreStore 屏障
- 1.1.2.StoreLoad 屏障
- 1.2.volatile读操作的内存屏障
- 1.2.1.LoadStore屏障
- 1.2.2.LoadLoad屏障
- 2.volatile不具备原子性
- 2.1.原理
Volatile原理
1.Volatile语义中的内存屏障
在Java代码中,volatile关键字主要又两层语义
- 不同线程对volatile变量的值具有内存可见性,就是一个线程修改了某个volatile变量的值,该值对其他线程立即可见。
- 禁止指令重排序
同时volatile关键字不仅能保证可见性,还能保证有序性,保证有序性是通过内存屏障指令来确保的。
JVM编译器会在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的CPU重排序。
JVM在处理volatile
关键字修饰的变量时,会采取保守策略来确保内存可见性和有序性,这涉及到内存屏障(Memory Barrier)的使用。内存屏障是一种硬件层面的指令,用于确保某些内存访问操作的执行顺序,防止CPU的乱序执行对并发程序的正确性产生影响。对于volatile
变量的读写,JVM会分别在读和写操作前后插入适当类型的内存屏障,确保以下几点:
- 全局可见性: 保证对
volatile
变量的写操作能立即对其他线程可见,即使在不同的CPU缓存中也是如此。这意味着写操作之后,修改的值会立刻刷出到主内存中。 - 禁止重排序: 防止编译器和CPU对涉及
volatile
变量的代码进行不必要的重排序,确保它们按照程序员指定的顺序执行。这对于依赖于特定顺序的并发控制逻辑至关重要。
基于保守策略的volatile
操作内存屏障插入策略主要包括以下方面:
- 写屏障(Store Memory Barrier): 在写入
volatile
变量之后插入。它的作用是确保在该屏障之前的所有普通写操作(非volatile
)都已完成,并且将当前线程的工作内存中的volatile
变量值刷新到主内存中。这样,任何读取该volatile
变量的线程都能看到最新值。- 在每个volatile
写
操作前
面插入一个StoreStore
屏障 - 在每个volatile
写
操作后
面插入一个StoreLoad
屏障
- 在每个volatile
- 读屏障(Load Memory Barrier): 在读取
volatile
变量之前插入。它的作用是确保读取操作之后的加载不会被重排序到该屏障之前,并且使CPU读取主内存中的最新值,而不是使用缓存中的旧值,从而确保读取到的是最近一次写入的值,无论这个写入操作发生在哪个线程中。- 在每个volatile
读
操作前
面插入一个LoadLoad
屏障 - 在每个volatile
读
操作后
面插入一个LoadStore
屏障
- 在每个volatile
这些屏障的联合使用确保了对volatile
变量的读写操作具有原子性和全局有序性,尽管它们不保证复合操作(如count++
)的原子性。这就是为什么即使在没有锁的情况下,volatile
也能作为轻量级的同步机制,用于状态标记、双重检查锁定模式等场景。
1.1.volatile写操作的内存屏障
volatile写操作的内存屏障插入策略为:在每个
volatile
写操作前插入SotreStore(SS)
屏障,在写操作之后加上StoreLoad
屏障
1.1.1.StoreStore 屏障
定义与作用: StoreStore
屏障主要用于确保一个存储(写)操作在另一个存储操作之前完成。换句话说,它强制所有在该屏障之前的存储操作在该屏障之后的存储操作之前完成。这种屏障通常用于避免写-写冲突导致的数据不一致问题,尤其是在处理器有乱序执行能力的体系结构中。
- 前面的写入不会重排序到后面
- 前面的写指令完成后,高速缓存数据刷入主存
- 后面的写操作不会排序到前面
应用场景: 例如,在实现某些类型的锁释放操作时,可能需要确保解锁操作前的所有写操作已经完成,以免新获得锁的线程看到不一致的状态。
1.1.2.StoreLoad 屏障
定义与作用: StoreLoad
屏障是Java内存模型中最强大的一种屏障,它确保在屏障之前的所有写操作(存储操作)在屏障之后的任何读操作(加载操作)之前完成。这意味着不仅要求写操作完成,而且要确保这些写操作对所有线程可见。因此,StoreLoad
屏障通常用于实现volatile
变量的写操作后,以确保写入的值对其他线程立即可见。
- 前面的写不会重排序到后面
- 前面的写指令操作完成后,高速缓存数据立即刷入主存
- 让高速缓存的数据失效,重新从主存中加载数据,保证内核的高速缓存数据一致
- 后面的读操作不会重排序到前面
应用场景: StoreLoad
屏障直接关联于Java中volatile
字段的写操作实现。当一个线程修改了一个volatile
变量的值,JVM会在写操作之后插入一个StoreLoad
屏障,以确保该写入的值能够立即对其他线程可见,同时刷新处理器的缓存,避免数据的脏读。此外,它也常用于锁释放操作后的内存可见性保障,确保解锁前的内存更改对后续可能获得锁的线程是可见的。
总结来说,StoreStore
屏障关注于维持存储操作之间的顺序,而StoreLoad
屏障则进一步确保了写操作的全局可见性,并在写-读操作间建立了一个顺序关系,这对于维护多线程程序的一致性和正确性至关重要。
1.2.volatile读操作的内存屏障
volatile读操作的内存屏障插入策略为:在每个volatile读操作后面插入
LoadLoad(LL)
屏障和LoadStore
屏障,禁止后面的普通读、普通写、和前面的volatile读操作发生重排序
1.2.1.LoadStore屏障
定义与功能: LoadStore
屏障,也称为读写屏障,其主要作用是确保屏障之后的读操作不会被重排序到屏障之前,且屏障之后的写操作不会被重排序到屏障之前的读操作之前。这意味着它不仅确保了读操作不会提前,还阻止了读之后的写操作与读操作之前的任何写操作发生乱序。这在volatile读操作的上下文中,意味着确保了读取到的volatile变量的值不会被之后的写操作所覆盖或影响,保持了读取操作的确定性。
- 前面的读操作不会排到后面
- 让高速缓存中的数据失效,重新从主存中加载数据
- 后面的写操作不会排列到前面
在volatile读操作中的应用: 当执行volatile读操作时,Java虚拟机(JVM)会在读取操作之后插入一个LoadStore
屏障。这个屏障的目的是确保当前线程的任何后续写操作不会与刚完成的volatile读操作交错,保证了volatile读的值不会因为之后的写而变得无效或不一致。同时,这也间接帮助确保了volatile读取操作后的写操作不会与之前的volatile读操作或普通读操作发生冲突,维护了操作的顺序性。
1.2.2.LoadLoad屏障
定义与功能: LoadLoad
屏障,或称为读读屏障,它的主要职责是防止屏障之后的读操作被重排序到屏障之前的任何读操作之前。这意味着它确保了在屏障之后执行的任何读操作不会因为CPU的乱序执行优化而提前到屏障之前执行。尽管LoadLoad
屏障本身在某些JMM的描述中不常直接提及,但讨论内存屏障时,其概念往往隐含在维护读操作顺序性的讨论中。
- 前面的读操作不会被排到后面
- 让高速缓存中的数据失效,重新从主存中加载数据
- 后面读操作不会排列到前面
在volatile读操作中的应用: 虽然直接提及LoadLoad
屏障在volatile读操作后插入的情况较少见,通常强调的是LoadStore
和StoreLoad
屏障的作用,但理解其概念对于全面把握内存屏障如何维护顺序性是有帮助的。在volatile读操作的上下文中,可以抽象理解为,屏障的逻辑效果确保了读取volatile变量的值不会被之后的其他读取操作提前,保证了读取volatile变量的顺序性。不过,实际中,volatile读操作的关键在于通过StoreLoad
屏障确保了对其他线程写入volatile变量的值立即可见,同时防止了volatile读与普通读写操作的不恰当重排序。
2.volatile不具备原子性
volatile能保证数据的可见性,但是volatile并不能完全保证数据的原子性。对于volatile类型的变量进行符合操作例如(i++),仍然会存在线程不安全的问题
/*** 使用 10个线程,每个线程进行1000次 ++操作,来观察成员变量的结果是否符合我们的预期*/
public class VolatileAddDemo {private volatile int num = 0;@Test@DisplayName("测试并发情况下 volatile原子性")public void testVolatileAdd() {CountDownLatch latch = new CountDownLatch(10);ExecutorService executorService = Executors.newFixedThreadPool(10);for (int i = 0; i < 10; i++) {executorService.submit(() -> {for (int j = 0; j < 1000; j++) {num++;}// 每次执行完毕 -1latch.countDown();});}// 等待全部线程执行完毕try {latch.await();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("最终的结果:" + num);System.out.println("预期的结果:10000");System.out.println("相差:" + (10000 - num));}}
2.1.原理
首先来看一下JMM对变量进行读取和写入的操作流程
对于非volatile修饰的普通变量来说,在读取变量的时候,JMM要求需要 报纸read
、load
的顺序即可
但是,从主存中读取 x 、y 两个变量的值,可能的操作是 read x
-> read y
-> load y
-> load x
,它并不要求操作是连续的
对于关键字 volatile修饰的内存可见变量而言,具有两个重要的语义
- 使用volatile修饰的变量在变量值发生改变时,会
立即同步到主存
,并且让其他线程的变量副本失效
- 禁止指令重排序:用volatile修饰的变量在硬件层面上会通过在指令前后加入内存屏障来实现,编译器通过以下规则来进行实现的。
- 使用volatile修饰的变量 **read(读取),load(加载),use(使用)**都是连续出现的,所以每次使用变量时都要从主存读取最新的变量值,替换私有内存的变量副本值
- 对于同一个变量的**assign(赋值),store(存储),write(主存)**操作都是连续出现,所以每次对变量的修改都会立即同步到主存中
?但是思考一下,单线程下**( read,load,use),(assign,store,write)**同时出现没什么问题,但是在多线程并发执行的情况下,因为单个操作具备原子性,但是多个组合的话就不具备原子性了,还是有可能会出现脏数据。
下面通过图来了解一下 并发时可能发生产生脏数据的场景
对于复合操作,volatile变量是无法保证其原子性的,如果想要保证复合操作的原子性,那么就需要使用锁,并在在高并发场景下,volatile变量一定要和Java显示锁结合使用
这里补充介绍一下 JMM内存模型的 8个 操作
操作 | 描述 | 作用的对象 |
---|---|---|
read | 读取 | 把一个变量的值从主内存或高速缓存读到线程的工作内存中,准备下一步的load操作 |
load | 加载入 | 把read操作从主内存读取的变量值放入线程的工作内存中的变量副本中,此时变量才对线程可见 |
use | 使用 | 把工作内存中变量的值传递给执行引擎,作为运算的输入 |
assign | 赋值 | 把执行引擎计算出的结果赋值给工作内存中的变量 |
store | 存储 | 把工作内存中修改后的变量值写回到主内存中 |
write | 写出 | 把store操作从工作内存中变量的值写入到主内存,使得其他线程可见 |
lock | 加锁 | 作用于主内存的变量,标记变量为线程独占,确保同一时刻只有一个线程能执行lock和unlock之间的操作 |
unlock | 解锁 | 释放锁,作用于主内存的变量,允许其他线程获取该变量的锁并进行操作 |
相关文章:

22.Volatile原理
文章目录 Volatile原理1.Volatile语义中的内存屏障1.1.volatile写操作的内存屏障1.1.1.StoreStore 屏障1.1.2.StoreLoad 屏障 1.2.volatile读操作的内存屏障1.2.1.LoadStore屏障1.2.2.LoadLoad屏障 2.volatile不具备原子性2.1.原理 Volatile原理 1.Volatile语义中的内存屏障 在…...
Vue 3中的v-for指令使用详解
Vue 3中的v-for指令使用详解 一、前言1. 基本语法2. 循环渲染对象3. 在组件中使用v-for4.普通案例5. 其他用法 二、结语 一、前言 在Vue 3中,v-for指令是一个非常强大且常用的指令,它用于在模板中循环渲染数组或对象的内容。本文将为您详细介绍Vue 3中v…...
GB-T 43694-2024 网络安全技术 证书应用综合服务接口规范
编写背景 随着网络技术的发展和信息化进程的加速,网络安全问题日益凸显。为了加强网络安全管理,提升网络服务的安全性和可靠性,GB-T 43694-2024《网络安全技术 证书应用综合服务接口规范》应运而生。这份文件是 网络安全领域的标准之一&…...
AI大模型:掌握未知,开启未来
AI大模型的工作原理 AI大模型是指通过大量数据和复杂算法训练出的能够理解和生成自然语言文本的人工智能模型。它们背后的核心技术主要包括深度学习、神经网络和自然语言处理。以下是详细的工作原理以及通俗易懂的类比: 1. 数据收集和预处理 AI大模型的训练首先需…...

【C语言习题】26.字符逆序
文章目录 1.描述2.解题思路3.具体代码 1.描述 输入描述: 将一个字符串str的内容颠倒过来,并输出。可以有空格 数据范围:1≤𝑙𝑒𝑛(𝑠𝑡𝑟)≤10000 1≤len(str)≤10000 输出描述&…...
windows和linux下的库文件比较
在Windows和Linux操作系统中,库文件(lib、dll、.a、.so)都扮演着重要的角色,但它们之间存在一些关键的区别。以下是这些库文件之间的主要差异: Windows lib 静态链接库(Static Link Library)…...
第七十九节 Java面向对象设计 - Java访问级别
Java面向对象设计 - Java访问级别 类简单名称是 class 关键字和 {)之间的名称。 当我们通过简单的名称引用一个类时,编译器在引用类所在的同一个包中查找该类声明。 我们可以使用全名来引用一个类如下。 com.w3cschool.Dog aDog;指定类的访问级别的一般语法是 &…...

Vue进阶之Vue项目实战(四)
Vue项目实战 出码功能知识介绍渲染器性能调优使用 vue devtools 进行分析使用“渲染”进行分析判断打包构建的产物是否符合预期安装插件使用位置使用过程使用lighthouse分析页面加载情况使用performance分析页面加载情况应用自动化部署与发布CI/CD常见的CI/CD服务出码功能 出码…...
fix leakage脚本
芯片的PPA追求是无止境的,因而在修时序的过程中我们需要对设计修复leakage,降低芯片的静态功耗。 以下分享一个典型的leakage脚本 set design 1 set version "V1" set date [exec date %m%d%H%M] set working_directory ${design}_${version}…...

MySQL中视图是什么,有什么作用
目录 一、视图的简介 1.1 什么是视图? 1.2 为什么使用视图? 1.3 视图有哪些规则与限制? 1.4 视图能否更新? 二、视图的创建 三、视图的作用 3.1 用视图简化复杂的联结 3.2 用视图格式化检索出的数据 3.3 用视图过滤数据…...
【面试题】JavaScript基础高频面试(下)
10、Javascript 闭包是什么,闭包形成的原因和闭包的用途 ? 闭包(Closure)是 JavaScript 中的一个非常重要的概念。简单地说,闭包就是一个函数能够访问另一个函数的作用域。这是因为在 JavaScript 中,函数是一等公民&a…...
对于个人而言,大数据时代如何更好地管理自己的信息?
在大数据时代,管理个人信息变得尤为重要。以下是几个建议来更好地管理个人信息: 认识和了解自己的数字足迹:了解自己在互联网上的活动,包括浏览历史、社交媒体和在线购物数据等。通过查阅自己的帐户设置和隐私选项,可以…...
oj项目后端分析
1.菜单管理 我们菜单管理有菜单表(sys_menu),还有用户角色表(sys_role),菜单表是用于管理我们用户所拥有的权限,不同的用户所看到的页面是不一样的,由于一些用户他能够看到题库管理和考题管理,还…...

书籍学习|基于SprinBoot+vue的书籍学习平台(源码+数据库+文档)
书籍学习平台 目录 基于SprinBootvue的书籍学习平台 一、前言 二、系统设计 三、系统功能设计 1平台功能模块 2后台功能模块 5.2.1管理员功能模块 5.2.2用户功能模块 5.2.3作者功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 …...
AI学习指南数学工具篇-MATLAB中的凸优化工具
AI学习指南数学工具篇-MATLAB中的凸优化工具 在人工智能领域,凸优化是一个非常重要的数学工具,它在机器学习、深度学习、数据分析等领域都有着广泛的应用。而MATLAB作为一款强大的数学工具软件,提供了丰富的凸优化工具和函数,为用…...

散户如何参与期权交易?
期权就是股票,唯一区别标的物上证指数,会看大盘吧,期权交易两个方向认购做多,认沽做空,双向t0交易没了,期权交易跟期货一样,对的,玩的也是合约,唯一区别没有保证金不会爆…...

Unity Apple Vision Pro 开发(一):开发前期准备【软硬件要求 | 开发者模式 | 无线调试打包】
文章目录 📕教程说明📕硬件要求📕软件要求⭐Xcode 15.2 及以上⭐visionOS 1.0 (21N301) SDK 或者更高版本⭐Unity 2022 LTS for Apple Silicon (2022.3.18f1及以上的版本)⭐Unity Pro/Unity Enterprise/Unity Industry的授权许可证 …...

IGMP——组播成员端网络协议
目录 一.IGMP基本概念 (1)组播转发困境 (2)感知组播成员方式 (3)IGMP版本 二.IGMP各版本的区别与联系 (1)IGMPV1 1.普遍组查询报文 2.成员关系报告报文 3.IGMPV1报文格式 4…...

Java break细节(标签)
Java break细节(标签)continue也可以使用标签 break是用来跳出循环的。 当有多重循环时,可以配合标签来使用,决定跳出那一重循环。 尽量不要使用标签 1、不代标签时,默认跳出 break 所在的那重循环: 可见在 i 3 时࿰…...

远程户外监控组网方案,工业4G路由器ZR2000
户外监控无人值守4G工业路由器组网应用涉及工业自动化、数据传输和远程监控的重要领域。在户外没有光纤的情况下,想要让监控或传感器等设备联网,仅需一台4G工业路由器即可解决。以下是关于远程监控户外组网的详细分析与应用: 物联网应用场景 …...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...

vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

均衡后的SNRSINR
本文主要摘自参考文献中的前两篇,相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程,其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt 根发送天线, n r n_r nr 根接收天线的 MIMO 系…...