尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
voliatile保证可见性的实现原理
一、问题根源CPU 缓存导致的可见性问题现代 CPU 为了性能有多级缓存架构关键问题每个核心有自己的 L1/L2 缓存。线程 A 在核心 0 修改了变量flag可能只存在于核心 0 的缓存中还没写回主内存此时线程 B 在核心 1 读取读到的可能是核心 1 缓存里的旧值。这就是可见性问题的物理根源。二、volatile的两层语义volatile在 JMMJava 内存模型中定义了两件事Table。语义含义可见性一个线程修改了volatile变量其他线程能立即看到最新值禁止指令重排序编译器和 CPU 不能将volatile变量周围的指令随意重排三、多线程修改共享数据的流程四、JVM层面的实现JVM 在编译 Java 代码时遇到volatile变量会在适当位置插入内存屏障指令。4.1 什么是内存屏障内存屏障是一类特殊的 CPU 指令作用是阻止屏障两侧的指令被重排序并强制刷新缓存。对volatile操作JVM 会插入四种屏障操作插入的屏障含义volatile写前面StoreStore屏障前面的普通写操作必须在 volatile 写之前完成volatile写后面StoreLoad屏障volatile 写必须在后面的读操作之前完成volatile读后面LoadLoad屏障volatile 读必须在后面的普通读之前完成volatile读后面LoadStore屏障volatile 读必须在后面的普通写之前完成最重要的一个是StoreLoad屏障它是实现可见性的关键。五、CPU层面的实现lock前缀指令JVM 的内存屏障最终要翻译成具体的 CPU 指令。在x86 架构最常见的服务器 CPU下volatile写操作最终会被编译为带lock前缀的汇编指令。5.1 典型汇编代码长什么样假设有这样的 Java 代码volatile int flag 0; void write() { flag 1; // volatile 写 }编译成 x86 汇编后大致长这样movl $0x1, flag # 把 1 写入 flag lock addl $0x0, (%rsp) # 这就是内存屏障的化身注意第二行lock addl $0x0, (%rsp)这是一个空操作加法加 0但它前面加了lock前缀。为什么用空操作因为x86 的StoreLoad屏障通常就是用lock前缀指令实现的。mfence指令也可以但早期 JVM 更常用lock addl。5.2lock前缀到底做了什么lock前缀不是真的锁住总线现代 CPU 已经优化了它的核心作用是① 触发缓存一致性协议MESI当 CPU 执行lock前缀指令时当前核心将修改后的缓存行Cache Line标记为Modified状态通过总线嗅探Bus Snooping广播通知其他核心我修改了某个地址的数据其他核心收到通知后将自己缓存中对应的缓存行标记为Invalid失效强制将当前修改写回主内存或至少确保其他核心能看到最新值主内存和cpu做交互是通过总线的总线嗅探机制让cpu监听总线。② 建立内存屏障lock指令本身具有全屏障效果确保lock指令之前的所有读写操作都已完成确保lock指令之后的读写操作都在它之后执行。5.3、【小结】Volatile 缓存可见性实现原理底层实现主要是通过汇编 lock 前缀指令它会锁定这块内存区域的缓存 (缓存行锁定) 并回写到主内存 IA-32 和 Intel 64 架构软件开发者手册对 lock 指令的解释1会将当前处理器缓存行的数据立即写回到系统内存。2这个写回内存的操作会引起在其他 CPU 里缓存了该内存地址的数据无效 (MESI 协议)3提供内存屏障功能使 lock 前后指令不能重排序六、MESI 缓存一致性协议核心中的核心这是现代 CPU 保证多核缓存一致性的协议四个状态状态缩写含义ModifiedM数据已修改仅当前核心有与主内存不一致ExclusiveE数据独占仅当前核心有与主内存一致SharedS数据共享多个核心都有与主内存一致InvalidI数据失效不能再使用必须重新加载七、一个volatile写的完整流程核心 0线程 A 执行 volatile 写 核心 1线程 B 执行 volatile 读 │ │ ▼ ▼ flag 1 读取 flag │ │ ▼ ▼ 缓存行状态变为 M 缓存行状态为 I已失效 │ │ ▼ ▼ lock 指令广播 Invalidate 信号 ────────────────▶ 收到信号标记为 I │ │ ▼ ▼ 写回主内存 ◄───────────────────────────────── 从主内存重新读取所以线程 B 下次读取flag时发现自己的缓存行是Invalid被迫从主内存或核心 0 的缓存重新加载因此一定能看到线程 A 修改后的最新值这就是volatile 可见性的物理本质。八、一句话总结volatile的可见性 JVM 内存屏障 CPUlock前缀指令 MESI 缓存一致性协议。本质上就是修改后强制刷新缓存并通知其他核心失效让其他线程下次读取时不得不从主内存重新加载。volatile保证可见性核心是靠CPU 的缓存一致性协议和lock前缀指令。首先线程修改变量其实先改的是自己的CPU 缓存不是直接写主内存。所以别的线程可能看不到。加了volatile之后JVM 会在写操作后面加一个内存屏障翻译成汇编就是一条带lock前缀的指令。这个lock指令有两个作用触发 CPU 的MESI 缓存一致性协议一是强制把当前缓存行的修改刷回主内存二是通知其他 CPU 核心你们缓存里这个值作废了。其他线程再来读的时候发现自己缓存失效了就会去主内存重新加载自然就读到最新值了。另外volatile还能禁止指令重排序保证代码不会被编译器乱优化。
RELATED

相关推荐

ASMR内容创作:从主观感知到数据驱动的系统性测试与优化指南

ASMR内容创作:从主观感知到数据驱动的系统性测试与优化指南

最近在整理 ASMR 内容创作流程时,发现很多创作者,尤其是刚入门的朋友,对于如何系统性地测试和优化自己的作品效果感到非常迷茫。大家常问:“我这段触发音效够‘上头’吗?”、“听众真的能放松吗?”、“我的…

📅 2026/10/9 13:19:50
VTJ引擎:ESP32物联网开发的事件驱动与状态机框架实践

VTJ引擎:ESP32物联网开发的事件驱动与状态机框架实践

1. VTJ核心引擎:一个被低估的开源宝藏最近在嵌入式开发圈子里,尤其是围绕ESP32的物联网项目,VTJ这个名字开始被越来越多地提及。它不像Arduino那样家喻户晓,也不像ESP-IDF那样官方正统,但如果你正在寻找一个能快速构建…

📅 2026/10/7 18:47:35
Desktop-Delta Bench:评估AI理解GUI状态转换能力的新基准

Desktop-Delta Bench:评估AI理解GUI状态转换能力的新基准

你有没有遇到过这样的场景:想用大模型帮你自动操作电脑——比如“帮我把桌面上的截图都移到‘截图’文件夹里”,或者“打开浏览器,搜索最近的天气”——结果模型要么卡住不动,要么一通乱点,完全没理解屏幕上发生了什么…

📅 2026/9/20 2:08:59
MORE NEWS

更多资讯

📰

Win10下原生部署MSDE 2000实战指南

简介:本资源是专为Windows 10(64位)系统用户定制的MSDE 2000数据库一键安装解决方案,面向需在现代系统中复用经典轻量级SQL Server引擎的开发人员、测试工程师及遗留系统维护者,有效解决原生MSDE 2000在Win10下因权限控…

📰

自动驾驶超车场景下的变时域MPC路径规划实践

1. 项目背景与核心挑战在自动驾驶技术快速发展的今天,超车场景下的路径规划一直是行业公认的难点。传统PID控制方法在动态环境中的适应性不足,而模型预测控制(MPC)凭借其滚动优化和反馈校正的特性,成为解决这一问题的理…

📰

高效处理大CSV文件的技术与实践

1. 大文件CSV处理的痛点与挑战 作为一名长期与数据打交道的工程师,我深知处理大型CSV文件时的痛苦。当文件大小超过1GB时,常规工具如Excel、文本编辑器甚至一些编程语言的pandas库都会变得异常缓慢,有时甚至直接崩溃。更糟糕的是,…

📰

人大金仓KingbaseES V8R6 Java适配全指南:驱动、连接池与事务避坑

简介:本资源是一套面向Java开发者与国产化数据库迁移工程师的人大金仓(KingbaseES)适配实践方案,聚焦于在信创环境下完成Java应用与国产数据库的连接、建表、数据操作等关键环节的兼容性改造。资源包含4个核心文件:1个…

📰

变时域MPC在自动驾驶超车场景中的优化与应用

1. 项目概述:变时域MPC在超车场景中的创新应用 超车动作作为日常驾驶中的高风险场景,传统控制算法往往采用固定时间窗口的预测方式,难以应对突发状况。我们团队基于变时域模型预测控制(Non-uniform predictive horizon MPC&#x…

📰

插入排序、希尔排序、选择排序底层原理与工程选型

1. 为什么这三种排序必须放在初阶第一课讲? 刚带完一届算法实训班,我让所有学员在开课第一天就手写三段代码:直接插入、希尔、选择。结果近七成的人卡在“为什么希尔排序的增量序列不能随便选”上,还有人把选择排序的“找最小值”…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬