理解 JVM 的 8 个原子操作与 `volatile` 的语义

理解 JVM 的 8 个原子操作与 volatile 的语义

在并发编程中,volatile 常常被提及。但要真正理解它的作用,需要先回到 Java 内存模型(JMM) 的基础:线程与主内存之间的交互。JMM 定义了 8 个原子操作 来描述这些交互过程,而 volatile 的语义正是基于它们实现的。


JVM 的 8 个原子操作

JMM 把 主内存 、工作内存 和 执行引擎 的数据交互拆分成了 8 个最小操作,这些操作是并发语义的基石:

  1. lock(锁定)

    将主内存中的变量标记为某个线程独占。

  2. unlock(解锁)

    解除独占标记,使变量可被其他线程访问。

  3. read(读取)

    从主内存中读取变量值,传输到线程的通道。

  4. load(载入)

    将 read 到的值写入线程工作内存的变量副本。

  5. use(使用)

    将工作内存中的值传给执行引擎使用。

  6. assign(赋值)

    将执行引擎计算后的值赋给工作内存中的变量副本。

  7. store(存储)

    将工作内存的值传输到通道。

  8. write(写入)

    将 store 传来的值写入主内存。


四组配对关系

这 8 个操作其实是 四组成对动作:

  • lock / unlock:保证变量在某一时刻只有一个线程独占。
  • read / load:把主内存的值带入工作内存。
  • store / write:把工作内存的值写回主内存。
  • use / assign:工作内存与执行引擎之间的数据交互。

示意图:

主内存 <──read/load──> 工作内存 <──use/assign──> 执行引擎

^ │

└─────────────store/write───────────┘

其中 use/assign 仅在 线程内部 发生,对跨线程的可见性影响不大,因此开发者平时不太关心它。


volatile 的语义

volatile 并不会改变 8 个原子操作的存在方式,而是通过 内存屏障 来影响其中部分操作的执行顺序和可见性:

  1. 保证可见性

    • 写入 volatile 变量:强制执行 store → write,立即刷新主内存。
    • 读取 volatile 变量:强制执行 read → load,直接从主内存取值。
  2. 保证有序性

    • 在 volatile 写操作前后,JVM 插入 StoreStore、StoreLoad 屏障,防止指令重排。
    • 在 volatile 读操作后,插入 LoadLoad、LoadStore 屏障,确保读到的值对后续操作可见。
  3. 不保证原子性

    • volatile 只能保证单次读/写的原子性。
    • 对于复合操作(如 count++),依然会分解成多步(read → load → use → assign → store → write),在多线程下可能出现 丢失更新。

为什么 use / assign 看似多余?

有人会觉得 use/assign 没有必要,因为我们写代码时从未直接操作过它。实际上:

  • read/load、store/write 关注的是 线程与主内存之间 的数据交换。
  • use/assign 关注的是 线程工作内存与执行引擎之间 的交互。

这部分虽然我们平时感知不到,但在 JMM 的规范 层面,它是必须被定义的,否则无法保证指令级别的精确定义。


总结

  • JVM 的 8 个原子操作分别是:lock / unlock / read / load / use / assign / store / write。
  • 它们可以配对为四组:
    • lock/unlock
    • read/load
    • store/write
    • use/assign
  • volatile 主要约束的是 read+load、store+write ,配合内存屏障保证 可见性 + 有序性。
  • volatile 不保证复合操作的原子性 ,如果需要保证,需要 CAS 或 锁。

✍️ 记住一句话:
volatile 让读写操作直达主内存,并通过内存屏障保证顺序,但不会把复合操作变成原子操作。

相关推荐
leisoo80973 分钟前
融资融券数据怎么查两融指标含义与杠杆观察方法 IG50免费开源股票数据API接口
开发语言·jvm·数据库·python·json
霸道流氓气质6 分钟前
多Agent通信机制与协议设计完全指南:从FIPA-ACL到A2A/MCP的Java生产级实战
java·开发语言
007张三丰12 分钟前
C/C++ 内存管理详解:从内存分布到 new/delete 底层原理
java·c语言·c++·内存管理
骇客野人30 分钟前
Java BIO / NIO / AIO 完整详解 + 编程技巧
java·开发语言·nio
霸道流氓气质33 分钟前
OpenTelemetry 入门与实战:Java Agent、Spring Boot Starter与LLM调用追踪示例
java·开发语言·spring boot
m0_5873830039 分钟前
24 小时自助健身房系统软件开发实战指南与案例解析
java·spring boot·spring·系统架构·需求分析
栖凤1 小时前
Java 的 try-catch 在 Agent 里失效了,我用了 5 个模式才兜住
java·前端·python
小师兄吃牛肉1 小时前
Java 同步系列之 ZooKeeper 分布式锁
java·分布式·java-zookeeper
马剑威(威哥爱编程)1 小时前
【AI全栈后端12-12】Spring Boot 3.x 到 4.x 迁移实操:Jakarta 11、Jackson 3 与 AI 2.0
java·开发语言·spring boot·后端
Gauss松鼠会1 小时前
【GaussDB】破除gaussdb ugin索引支持中文模糊查询的迷思-字符序
java·运维·服务器·网络·数据库·gaussdb·经验总结