理解 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 让读写操作直达主内存,并通过内存屏障保证顺序,但不会把复合操作变成原子操作。

相关推荐
Never_Satisfied1 分钟前
在 JavaScript 中,删除数组中内容为xxx的元素
java·前端·javascript
MC丶科9 分钟前
【SpringBoot常见报错与解决方案】端口被占用?Spring Boot 修改端口号的 3 种方法,第 3 种 90% 的人不知道!
java·linux·spring boot
怪兽201413 分钟前
Redis常见性能问题和解决方案
java·数据库·redis·面试
zz-zjx15 分钟前
JVM 内存结构与 GC 机制详解( 实战优化版)
java·jvm·tomcat
nvvas26 分钟前
Android Studio JAVA开发按钮跳转功能
android·java·android studio
CV工程师丁Sir43 分钟前
Rokid设备连接全解析:蓝牙与Wi-Fi通信源码深度剖析
java
zoyation1 小时前
多线程简介和在JAVA中应用
java·开发语言
rechol1 小时前
类与对象(中)笔记整理
java·javascript·笔记
周杰伦_Jay1 小时前
【Spring Boot从入门到精通】原理、实战与最佳实践
java·spring boot·后端
呼哧呼哧.1 小时前
SpringBoot 的入门开发
java·spring boot·后端