今天就内存模型作最后的梳理Java 共享内存模型下线程通信的唯一标准底层流程也是之前所有 JMM 核心抽象的实际落地—— 共享内存模型的 “隐式通信”本质就是通过「线程本地内存与主内存的共享变量同步」完成的这两步流程是多线程通过共享变量交换信息的唯一间接路径所有基于共享变量的线程协作最终都要遵循这个底层逻辑。这个流程的核心是 **「刷新 - 读取」的双向同步 **而同步机制volatile/synchronized/ 原子类的唯一作用就是强制保证这两步流程的「及时、可靠执行」如果没有同步机制约束这两步会因 JMM 的默认 “同步延迟” 执行不及时导致线程通信失败也就是之前讲的可见性问题。下面结合你的核心流程补充每一步的执行细节、同步机制的约束作用、代码示例验证、与消息传递模型的对比让你直观理解 “流程如何工作”“为何会通信失败”“同步机制如何修复”。一、共享内存模型首先明确一个关键前提共享内存模型中线程间没有任何直接通信的方式线程 A 无法直接访问线程 B 的本地内存所有通信都必须以主内存为中介通过「本地内存→主内存→本地内存」的间接路径完成而你梳理的两步就是这个间接路径的标准化拆解。这个流程的设计初衷是兼容 CPU 硬件的缓存特性利用本地内存 / 缓存提升线程操作效率但也因此引入了 “同步延迟” 的问题 ——JMM 默认不会强制线程立即执行这两步而是由 JVM/CPU 按需调度这是可见性问题的直接根源。二、线程通信两步流程结合 JMM 的主内存 / 本地内存抽象对每一步的执行主体、操作内容、默认行为、同步约束做精准拆解核心关注 **「默认行为」和「同步约束」的差异 **这是通信成败的关键。1、线程 A 将本地内存中更新的共享变量刷新到主内存执行主体修改变量的写线程线程 A操作内容线程 A 将自己本地内存中修改后的共享变量副本同步回主内存覆盖主内存中该变量的旧值让主内存保持「最新值」JMM 默认行为不立即执行—— 写操作会先存在 CPU 写缓冲区再批量、延迟刷入主内存提升执行效率线程 A 执行完变量新值后可能很久都不执行这一步同步机制的强制约束加了volatile/synchronized后会立即强制执行这一步volatile写操作后立即强制刷新无延迟synchronized线程释放锁时退出同步代码块立即强制刷新所有在临界区修改的共享变量。2、线程 B 从主内存读取线程 A 更新后的共享变量到本地内存执行主体读取变量的读线程线程 B操作内容线程 B 放弃自己本地内存中该共享变量的旧副本从主内存重新拷贝最新值到自己的本地内存生成新副本后续操作基于新副本执行JMM 默认行为不立即执行—— 线程 B 会优先读取自己本地内存的旧副本缓存命中不会主动去主内存刷新即使主内存已经有了新值同步机制的强制约束加了volatile/synchronized后会立即强制执行这一步volatile读操作前立即强制从主内存刷新放弃旧副本synchronized线程获取锁时进入同步代码块立即强制从主内存刷新所有共享变量的最新值。流程本质线程 A 的 “写通信” 是本地→主存的单向同步线程 B 的 “读通信” 是主存→本地的单向同步两者结合就是完整的线程通信缺一步则通信失败。三、验证流程用最经典的线程停止标记示例直观演示「无同步时流程延迟导致通信失败」以及「加 volatile 后强制流程及时执行实现通信成功」让你看到流程和同步机制的直接关联。无同步机制 —— 流程执行延迟线程通信失败可见性问题public class CommunicationFail { // 共享变量主内存初始值false无任何同步约束 private static boolean stopFlag false; public static void main(String[] args) throws InterruptedException { // 线程B读线程循环读取stopFlag期望收到停止信号 new Thread(() - { System.out.println(线程B开始运行等待停止信号...); // 【步骤2未强制执行】线程B一直读取自己本地内存的旧副本false从不主动刷新主内存 while (!stopFlag) { // 空循环无任何操作 } System.out.println(线程B收到停止信号停止运行); }, 线程B).start(); // 主线程休眠1秒让线程B先启动并进入循环 Thread.sleep(1000); // 线程A主线程写线程修改stopFlag为true stopFlag true; System.out.println(线程A已发送停止信号stopFlagtrue); // 结果线程B会无限循环永远收不到停止信号通信失败 } }失败根源线程 A主线程修改stopFlagtrue后JMM 默认未立即执行步骤 1刷新到主内存修改仅停留在主线程的本地内存线程 B一直默认执行旧逻辑步骤 2 未强制执行反复读取自己本地内存中stopFlagfalse的旧副本从不主动去主内存刷新两步流程都因 “同步延迟” 未执行最终导致线程 A 的信号无法传递给线程 B通信失败。加 volatile 同步 —— 强制流程及时执行线程通信成功仅需给共享变量加volatile关键字就能强制约束两步流程立即、可靠执行实现通信成功public class CommunicationSuccess { // 加volatile强制约束步骤1和步骤2的及时执行 private static volatile boolean stopFlag false; public static void main(String[] args) throws InterruptedException { new Thread(() - { System.out.println(线程B开始运行等待停止信号...); while (!stopFlag) { // 空循环 } System.out.println(线程B收到停止信号停止运行); }, 线程B).start(); Thread.sleep(1000); // 线程A修改volatile强制执行【步骤1】立即刷新到主内存 stopFlag true; System.out.println(线程A已发送停止信号stopFlagtrue); // 结果线程B立即收到信号退出循环通信成功 } }成功核心volatile对共享变量的双向强制约束对写线程 A修改stopFlagtrue后立即强制执行步骤 1将新值从本地内存刷新到主内存主内存瞬间更新为最新值对读线程 B每次判断!stopFlag时立即强制执行步骤 2放弃本地内存的旧副本从主内存读取最新值两步流程被强制 “即时执行”线程 A 的信号通过主内存顺利传递给线程 B通信完成。synchronized 的约束逻辑基于锁的释放 / 获取synchronized同样能保证这个流程的可靠执行只是约束方式是基于锁的「释放 - 获取」成对操作比 volatile 的粒度更粗同时保证原子性、有序性private static boolean stopFlag false; private static final Object lock new Object(); // 线程A写线程释放锁时强制执行步骤1 synchronized (lock) { stopFlag true; // 修改变量 } // 释放锁强制将stopFlag的新值刷新到主内存步骤1 // 线程B读线程获取锁时强制执行步骤2 while (true) { synchronized (lock) { // 获取锁强制从主内存读取stopFlag的最新值步骤2 if (stopFlag) { break; } } }核心逻辑线程释放锁时JMM 会强制将该线程本地内存中所有修改的共享变量刷新到主内存步骤 1线程获取锁时JMM 会强制将该线程本地内存的共享变量副本失效必须从主内存重新读取步骤 2。四、两步流程需要注意的是你梳理的 **“步骤 1→步骤 2” 是一个连续的通信逻辑但并非原子操作 **—— 两步之间可能存在其他线程的介入这也是为什么复合操作需要保证原子性比如多个线程同时修改一个共享变量count线程 A 执行步骤 1 刷新count1后线程 C 可能立即执行步骤 1 刷新count2线程 B 此时执行步骤 2读取到的会是线程 C 的最新值而非线程 A 的。这种情况并非通信失败而是多线程竞争下的正常结果同步机制的作用是保证 “读取的一定是主内存的最新值”而原子类AtomicXxx的作用是保证 “修改变量 刷新主内存” 的步骤 1 是原子操作避免多线程修改时的数值覆盖。五、与消息传递模型的通信流程对比强化理解为了更清晰的区分两大通信模型的核心差异把共享内存模型的这个底层流程和消息传递模型的通信流程做对比能更直观看到 “隐式通信” 和 “显式通信” 的本质区别通信模型核心通信流程通信媒介同步约束方式核心特点共享内存模型线程 A本地→主存步骤 1线程 B主存→本地步骤 2堆内存的共享变量同步机制强制流程即时执行隐式通信、间接主存为中介消息传递模型线程 A显式发送消息入队线程 B显式接收消息出队阻塞队列 / 消息队列框架内置阻塞机制满 / 空阻塞显式通信、直接队列做媒介核心差异共享内存模型的通信是隐式的—— 线程无需知道 “谁在和我通信”只需读写共享变量JMM 自动完成底层的刷新 / 读取消息传递模型的通信是显式的—— 线程必须通过put()/take()等 API 显式发送 / 接收消息通信行为完全由开发者控制。最后小结你梳理的「刷新主存→读取主存」两步是共享内存模型下线程通信的唯一底层流程所有基于共享变量的隐式通信都遵循这个逻辑主内存是唯一的通信中介流程的核心是共享变量的「双向同步」线程间无直接通信能力只能通过本地内存与主内存的同步完成信息交换同步机制volatile/synchronized/ 原子类的核心作用是强制保证这两步流程的及时、可靠执行消除 JMM 默认的 “同步延迟”解决可见性问题无同步机制时流程会因延迟执行导致通信失败线程间无法感知彼此的修改加同步机制后流程被强制即时执行实现通信成功两步流程是逻辑连续的但非原子操作多线程竞争下可能有其他线程介入复合操作需额外保证原子性该流程是共享内存模型独有的与消息传递模型的 “显式发送 - 接收” 形成本质区别也是 Java 并发问题的核心来源。