java内存模型(jmm)是必须理解并应用的并发语义规则,核心在于掌握抽象模型、识别问题、编写合规代码;它通过主内存与工作内存抽象及八种原子操作定义可见性与有序性,始终生效而非可配置。

Java 内存模型(JMM)不是靠“配”出来的配置项,而是一套必须被理解、被应用的并发语义规则。它不提供开关、参数或 XML 标签让你一键启用——所谓“通关秘籍”,本质是掌握三类关键能力:看懂抽象模型、识别真实问题、写出符合模型约束的代码。
一、先搞清 JMM 的两个核心抽象:主内存 ≠ 堆,工作内存 ≠ 栈
- 主内存是逻辑概念,代表所有线程共享变量的“权威副本”所在,大致对应堆中对象字段、方法区静态字段;
- 工作内存是每个线程私有的操作副本空间,不是物理内存块,也不等于 Java 虚拟机栈——它包含线程对变量的临时拷贝,可能来自寄存器、CPU 缓存或栈帧局部变量;
- 所有变量读写都必须经过八种原子操作(read/load/use/assign/store/write/lock/unlock),JMM 正是通过这些操作的顺序约束定义可见性与有序性。
✅ 关键提醒:不要试图在 JVM 启动参数里“开启 JMM”。它始终生效,你唯一能做的,是让代码行为符合它的规则。
二、用 volatile 和 synchronized 锚定 happens-before 关系
这是 JMM 最实用的落地抓手:
-
volatile写 → 后续任意线程的volatile读:建立 happens-before,保证之前所有普通写对读线程可见; -
synchronized解锁 → 后续同一锁的加锁:保证临界区内的所有操作对下一个获得锁的线程可见; - 构造器完成 → 新对象引用被其他线程看到(前提是安全发布,如 final 字段初始化、volatile 引用写、或锁内发布)。
✅ 实操建议:
- 状态标志(如
running = false)用volatile足够;- 多字段协同变更(如订单状态 + 时间戳 + 操作人)必须用锁或
AtomicReference<status></status>封装;- 不要用
volatile替代synchronized做复合逻辑,比如if (count == 0) count++—— 这仍是竞态。
三、防伪共享 ≠ 调字段顺序,而是控内存布局
JVM 会按字段大小自动重排(long/double 优先),源码声明顺序 ≠ 实际内存布局。真正有效的是:
- 手动填充:在高争用
volatile long前后各加 7 个long字段(56 字节),凑满 64 字节缓存行隔离; -
@Contended注解(JDK 8+):需启动参数-XX:+UnlockExperimentalVMOptions -XX:+UseContended,JVM 自动插入填充; - 结构拆分:把竞争字段放到不同对象中(如
LongAdder的 cells 数组),利用对象对齐天然拉开距离。
✅ 验证比猜测重要:用 JOL 查偏移量,用 JMH 测吞吐变化,用
perf stat -e cache-misses观察缓存未命中率。
四、避免总线风暴:减热点、升粒度、断伪共享
高频修改同一 volatile 变量(如全局计数器、单点队列头尾)会触发大量 MESI 广播,引发缓存行失效风暴。应对策略:
- 用分段结构替代全局变量:
LongAdder、ConcurrentHashMap的分段扩容、MPSC 队列分离 head/tail; - 把轮询改成等待:
while (!done) Thread.yield()→LockSupport.parkNanos()或条件变量; - CAS 失败时不盲目重试:加入
Thread.onSpinWait()或指数退避,降低无效嗅探频率。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











