JMM是编程语义契约而非配置项,核心在于建立happens-before关系、正确声明共享变量、显式同步协作;volatile保可见性与有序性但不保原子性,synchronized保原子性/可见性/有序性,final保证初始化安全。

明确哪些变量必须受 JMM 约束
只有被多个线程读写的共享变量才需要 JMM 保障。局部变量、方法参数、不可变对象(如 String、Integer 常量)天然线程安全,不用额外处理。
- 重点关注:
static字段、堆上对象的可变字段(如user.name = "Alice")、缓存类(如ConcurrentHashMap外的普通Map) - 典型陷阱:把
ArrayList当全局列表用,没加锁也没用线程安全替代品 - 判断标准:该变量是否可能被两个及以上线程,在未同步的情况下,一个写、一个读(或两个都写)
用对 volatile、synchronized 和 final
它们不是性能开关,而是内存语义开关:
-
volatile:只保可见性 + 有序性(禁止重排序),不保原子性。适合状态标志(如running = false)、单次写入后只读的引用(如懒汉单例中的 instance) -
synchronized:保原子性 + 可见性 + 有序性。进入/退出时清空/刷新工作内存,是最稳妥的同步原语 -
final字段:构造器内一次性赋值后,其他线程看到的一定是初始化完成的值(JMM 保证 final 域的初始化安全),这是不可变设计的基石
别依赖“看起来会按顺序执行”
编译器和 CPU 都可能重排序。哪怕代码写成 A → B → C,JMM 不保证其他线程看到这个顺序,除非有 happens-before 链。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 常见错误:双重检查锁(DCL)单例中,
instance = new Singleton()被拆成“分配内存→初始化→赋值”,若无 volatile,其他线程可能拿到未初始化完成的对象 - 正确做法:用
volatile Singleton instance或直接用静态内部类(由类加载机制保证 happens-before) - 记住:happens-before 是唯一可靠的因果链。启动线程、加锁、volatile 写读、Thread.join() 都自带 happens-before 关系
验证比猜测更可靠
写完并发逻辑,别只靠脑子想“应该没问题”。用工具看真实行为:
立即学习“Java免费学习笔记(深入)”;
- 加
-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly(需 hsdis)看 JIT 是否真的插入了内存屏障 - 用 JOL(Java Object Layout)查看对象内存布局,确认 volatile 字段是否真的独占 cache line(防伪共享)
- 用 JCStress 框架跑并发压力测试,它能暴露 99.9% 的竞态条件,比手工多线程测试靠谱得多

















