Java 内存模型(JMM)定义的抽象逻辑操作
答案是否定的:read/load、store/write 是Java内存模型(JMM)定义的抽象逻辑操作,其操作目标是JMM的主内存和工作内存,而非直接指向物理的CPU缓存;但这些抽象操作的底层物理执行过程,会依托CPU缓存(L1/L2/L3)+主存完成——简单说,二者是抽象层规则和物理层实现的对应关系,而非直接等价关系。
你会有这个疑问,本质还是因为混淆了JMM的抽象内存模型和CPU的物理缓存架构,而read/load/store/write正是JMM为了屏蔽硬件差异,定义的线程操作变量的标准化抽象步骤,也是理解工作内存和主内存交互的核心。
下面我们先讲清这四个抽象操作的原生JMM语义,再对应到CPU缓存的物理执行过程,彻底说清二者的关联和区别。
一、先明确:JMM中read/load/store/write的抽象逻辑语义
JMM规定:线程对所有变量的操作,必须在自身工作内存中进行,不能直接操作主内存。而read/load是主内存 → 工作内存的变量加载抽象步骤,store/write是工作内存 → 主内存的变量同步抽象步骤,四个操作是成对出现、按序执行的,且均为原子的、不可分割的操作(JMM层面)。
1. 主内存 → 工作内存:read + load(变量加载)
- read:JMM抽象操作,从主内存中读取变量的原始值,并将该值传输到线程的工作内存缓冲区(工作内存的临时区域),仅完成“主存→工作内存的传输”,不涉及变量副本的初始化。
- 操作目标:JMM抽象的主内存(对应物理的主存+L3共享缓存);
- 操作结果:变量值进入工作内存缓冲区,未与工作内存中的变量副本绑定。
- load:JMM抽象操作,将工作内存缓冲区中通过read获取的变量值,加载到工作内存的变量副本中,完成后线程才能对该变量副本进行读/写操作。
- 操作目标:JMM抽象的工作内存;
- 操作结果:工作内存中生成/更新变量副本,线程可直接操作。
核心规则:read必须在load之前执行,且二者一一对应(读一个变量的主存值,就必须加载到工作内存的变量副本)。
2. 工作内存 → 主内存:store + write(变量同步)
- store:JMM抽象操作,将工作内存中修改后的变量副本值,传输到工作内存的缓冲区,仅完成“工作内存→主存的传输准备”,不涉及主内存的变量更新。
- 操作目标:JMM抽象的工作内存;
- 操作结果:修改后的变量值进入工作内存缓冲区,等待写入主内存。
- write:JMM抽象操作,将工作内存缓冲区中通过store获取的变量值,写入到主内存的原始变量中,完成后其他线程才能通过read/load获取该变量的最新值。
- 操作目标:JMM抽象的主内存;
- 操作结果:主内存中的变量值被更新,实现工作内存到主存的同步。
核心规则:store必须在write之前执行,且二者一一对应;同时,线程对变量副本的修改(assign操作)必须在store之前执行(修改后才能同步)。
补充:JMM的完整变量操作流程
结合线程对变量的修改(assign)和使用(use),一个线程修改变量并同步回主存的完整JMM抽象流程为:
read(主存) → load(工作内存) → use(线程操作变量副本) → assign(修改变量副本) → store(工作内存) → write(主存)
所有操作均为JMM的逻辑步骤,与具体的物理硬件(CPU缓存、寄存器)无关。
二、再对应:抽象操作在CPU缓存架构中的物理执行过程
JMM的抽象操作最终需要依托CPU物理硬件执行,而工作内存的物理载体是CPU L1/L2私有缓存+寄存器,主内存的物理载体是内存条+CPU L3共享缓存——因此,read/load和store/write的物理执行,会深度依赖CPU缓存,只是并非直接“从CPU缓存读”或“写到CPU缓存”,而是通过CPU缓存完成抽象的“主存-工作内存”交互。
下面结合CPU缓存架构(寄存器→L1→L2→L3→主存)和MESI协议,讲清两个核心流程的物理执行细节。
1. read + load 的物理执行(主存→工作内存,抽象加载 → 物理缓存读取)
抽象的read(主存) → load(工作内存),物理上是CPU从“主存/L3”加载数据到“L1/L2/寄存器”的过程,也是CPU缓存命中/失效的核心场景,具体步骤:
- 线程发起变量读取请求,CPU先检查当前核心的L1私有缓存中是否有该变量所在的缓存行(以缓存行为单位,而非单个变量):
- 若缓存命中(L1有有效缓存行):直接从L1读取变量数据,跳过后续步骤;
- 若缓存失效(L1无/缓存行为Invalid):CPU检查L2私有缓存,再检查L3共享缓存,最后检查主存。
- 若L3/主存有该变量的缓存行,CPU通过总线将该缓存行加载到当前核心的L2→L1缓存,并根据MESI协议标记缓存行状态(Exclusive/Shared);
- 若变量被频繁访问,CPU会将其从L1缓存进一步加载到寄存器(速度最快);
- 物理上,L1/L2/寄存器中存储的变量数据,就是JMM抽象的工作内存中的变量副本——即完成了
load操作的抽象语义; - 整个物理过程中,CPU从L3/主存加载数据的行为,对应JMM抽象的
read操作;数据进入L1/L2/寄存器的行为,对应JMM抽象的load操作。
关键结论:抽象的read/load不是“直接从CPU缓存读”,而是物理上优先从CPU缓存(L1→L2→L3)读,缓存无数据时再从主存读,最终将数据加载到工作内存的物理载体(L1/L2/寄存器)中。
2. store + write 的物理执行(工作内存→主存,抽象同步 → 物理缓存刷新)
抽象的store(工作内存) → write(主存),物理上是CPU将“L1/L2/寄存器”中的修改数据刷写到“L3/主存”的过程,也是volatile可见性的核心执行环节,分普通变量和volatile变量两种场景(核心差异是是否有内存屏障强制刷新):
场景1:普通变量的store + write(异步刷缓存,延迟同步)
- 线程修改变量副本,物理上是修改当前核心L1/L2缓存中的缓存行数据(或寄存器),CPU根据MESI协议将缓存行标记为Modified(修改态);
- 抽象的
store操作,物理上是将修改后的缓存行数据放入CPU的写缓冲(Store Buffer)(核心私有,提升硬件效率); - 抽象的
write操作,物理上是CPU异步将写缓冲中的数据刷写到L3共享缓存/主存(非即时),此时其他核心的缓存行仍可能为有效状态,导致可见性问题; - 整个过程中,修改L1/L2并放入写缓冲对应
store,异步刷写到L3/主存对应write。
场景2:volatile变量的store + write(内存屏障强制刷新,即时同步)
为保证可见性,JVM会在volatile写操作后插入StoreStore+StoreLoad内存屏障,强制突破CPU的写缓冲优化,让write的抽象语义即时物理生效:
- 线程修改
volatile变量副本,物理上修改L1/L2缓存的缓存行,标记为Modified; StoreStore屏障:禁止普通变量的写操作重排到volatile写之后,保证volatile变量的修改先完成;StoreLoad屏障:强制清空CPU写缓冲,将L1/L2中修改后的缓存行即时刷写到L3/主存,完成物理上的write;- CPU通过总线嗅探向其他核心发送失效通知,其他核心将该变量的缓存行标记为Invalid;
- 整个过程中,内存屏障让抽象的
store/write从异步物理执行变为同步物理执行,保证了volatile的可见性。
关键结论:抽象的store/write不是“直接写到CPU缓存”,而是先写到CPU L1/L2私有缓存,再通过异步/同步方式刷写到L3/主存,write的物理目标是L3/主存(对应JMM的抽象主存),而非CPU私有缓存。
三、核心误区总结:抽象操作 ≠ 物理操作
你产生这个疑问的核心,是将JMM的抽象逻辑操作与CPU的物理硬件操作直接画等号,而二者的核心区别体现在3点:
- 操作目标不同:
read/load/store/write的操作目标是JMM抽象的主内存/工作内存,是逻辑概念;物理操作的目标是CPU缓存(L1/L2/L3)+主存+寄存器,是硬件实体。 - 操作粒度不同:JMM抽象操作的粒度是Java变量(int、Object引用、数组元素);CPU物理操作的粒度是缓存行(通常64字节),即使是1个4字节的int变量,也会以缓存行为单位加载/刷新。
- 执行方式不同:JMM抽象操作是严格按序、原子执行的规则;CPU物理操作会有缓存命中/失效、写缓冲、无效队列等硬件优化,可能异步执行,JVM需要通过内存屏障让物理执行贴合JMM的抽象规则。
四、一句话总结
read/load不是直接从CPU缓存读,store/write也不是直接写到CPU缓存;二者是JMM为屏蔽硬件差异定义的、主存与工作内存交互的抽象逻辑操作,其底层物理执行过程会依托CPU L1/L2/L3缓存完成,最终物理目标是L3/主存(对应JMM抽象主存)和L1/L2/寄存器(对应JMM抽象工作内存)。
而JVM实现volatile可见性的核心,就是通过内存屏障强制CPU的物理执行行为严格贴合store/write的JMM抽象语义(即时刷主存),再结合MESI协议让其他核心的缓存行失效,最终实现多线程的可见性。

浙公网安备 33010602011771号