← ReJava · 学习目录

REJAVA / LEARNING NOTES

速查:JMM(Java 内存模型) · volatile(可见性关键字) · synchronized(内置同步关键字)

随时回查术语、规则和示例;完整解释见对应课程。

Java 17 / 21更新于 2026.10.12约 6 分钟阅读

根据课程材料整理

本次更新说明
  • 补充 Executor(执行器接口)的中文释义;改写结尾的复习提示。
  • 整理为公开学习笔记,保留技术内容与练习。
  • 清理个人分析,完善阅读导航。

三大特性

特性一句话谁保证
复合操作原子性操作不可分割;用锁保护复合操作时,相关访问须遵守同锁协议,异常不自动回滚synchronized(内置同步关键字)、Lock(显式锁接口);原子类的指定操作
可见性同步关系成立时,先前写入对后续读取可见volatile(可见性关键字)、synchronized(内置同步关键字)、Lock(显式锁接口)
有序性并发观察结果遵守内存模型的顺序约束volatile(可见性关键字,约束相关重排)、synchronized(内置同步关键字)

JMM(Java 内存模型)

  • 一组 规范 :抽象线程与主内存的关系,规定源码到 CPU(处理器) 指令要遵守的并发原则
  • 主内存、本地内存都是 规范层面的抽象 (不与某块物理内存一一对应)
  • ≠ JVM(Java 虚拟机) 内存区域 :内存区域(堆/栈/方法区)讲「数据放哪」
  • happens-before(先行发生关系) :A 的结果对 B 可见, 不是 时间先后

happens-before(先行发生关系) 常用规则

  1. 程序顺序 :同一线程内,前面的 hb 后面的
  2. 监视器锁 :解锁 hb 随后对 同一个 监视器的加锁
  3. volatile(可见性关键字) :写 hb 随后对同一变量的读
  4. 线程启动 :start() hb 新线程里的每个动作
  5. 线程终止 :线程的所有动作先行发生于另一方确认其终止;例如无超时的 join() 正常返回
  6. 传递性 :A hb B,B hb C ⇒ A hb C

另有线程中断等规则;并发容器、Executor(执行器接口)、Future(异步结果句柄) 也提供 hb 保证(JDK 文档)。

volatile(可见性关键字)

  • 可见性 :对同一变量的写先行发生于后续读,连带发布前的写入可见
  • 禁止重排序 :约束与该变量读写相关的重排序,不是禁止所有重排序
  • 不保证复合操作原子性 :i++ 仍然不安全
  • 屏障(理解用的模型,JVM(Java 虚拟机) 可合并或省略):写前 StoreStore(写写屏障)、写后 StoreLoad(写读屏障);读后 LoadLoad(读读屏障) + LoadStore(读写屏障)
  • DCL(双重检查锁定) :防「初始化」和「引用赋值」之间的重排;第一次检查在锁外,所以必须加

synchronized(内置同步关键字):用法与底层

  • 实例方法 → this(当前实例);静态方法 → 该类的 Class(类对象类型) 对象;代码块 → 括号表达式指向的对象;不能修饰变量
  • 互斥只约束竞争 同一对象监视器 的线程,不能阻止不拿这把锁的线程访问字段
  • 静态与实例同步方法默认使用不同监视器;方法内若另用共同锁,仍需单独判断。 可重入 指当前持有者再次获取同一监视器
  • 代码块:monitorenter(进入监视器指令) 与 monitorexit(退出监视器指令);简单示例含正常与异常释放路径,数量不是固定规则
  • 方法:ACC_SYNCHRONIZED(同步方法访问标志) 标志;两者本质都是获取对象的监视器
  • wait/notify 必须持有该对象的监视器,否则 IllegalMonitorStateException

volatile(可见性关键字) vs synchronized(内置同步关键字)

volatile(可见性关键字)synchronized(内置同步关键字)
用在哪字段,不用于局部变量方法、代码块
复合操作原子性✗相关访问遵守同一锁协议时保证
可见性✓ volatile(可见性关键字) 规则✓ 监视器锁规则
会阻塞不会竞争时会
怎么选只要可见性(标志位、DCL(双重检查锁定) 引用)要复合操作原子性(i++、先检查后执行)

两者互补:volatile(可见性关键字) 读写有类似进出监视器的内存效果,但不互斥(JDK 文档)。

锁状态 × JDK 版本(2026 必须按新说法答)

JDK偏向锁synchronized(内置同步关键字) 可能的锁状态
8 / 11默认开启无锁 → 偏向锁 → 轻量级锁 → 重量级锁
15默认关闭 + 标记废弃(JEP 374)—
17默认关闭;手动开启仍生效,但有废弃警告默认:无锁 → 轻量级锁 → 重量级锁
18彻底移除,命令行打不开—
21 / 25参数不认:带上就 Unrecognized VM option,启动失败无锁 → 轻量级锁 → 重量级锁
  • 轻量级锁 :竞争很轻时用 CAS(比较并交换) 加锁,不让操作系统挂起线程; 重量级锁 :依赖操作系统互斥量,抢不到就挂起(要切到内核态)
  • 为什么去掉偏向锁 :现代应用收益下降,竞争时撤销有额外成本;JVM(Java 虚拟机) 实现复杂、维护与演进困难。见 JEP 374 官方存档。
  • 升级坑 :-XX:+UseBiasedLocking、-XX:BiasedLockingStartupDelay=0 在 JDK 17 只警告,在 JDK 21 直接启动失败(本机实测)
  • 虚拟线程(后续专题) :JDK 21 上在 synchronized(内置同步关键字) 里阻塞会钉住载体线程;本机 JDK 25 实测已不再出现

一句话总结

  • volatile(可见性关键字) :「写 hb 后续读,约束相关重排序;不保证复合操作原子性。」
  • synchronized(内置同步关键字) 原理 :「代码块 monitorenter(进入监视器指令)/monitorexit(退出监视器指令),方法 ACC_SYNCHRONIZED(同步方法访问标志),本质都是获取对象的监视器。」
  • synchronized(内置同步关键字) 可见性 :「解锁 hb 随后对同一个监视器的加锁。」
  • DCL(双重检查锁定) :「synchronized(内置同步关键字) 只保护拿同一把锁的线程;第一次检查在锁外,所以还要 volatile(可见性关键字)。」
  • 锁升级 :「JDK 8 是四种状态;偏向锁 15 默认关、18 移除;17 默认关闭但可显式启用;21 已移除。」

常见错误说法

  • volatile(可见性关键字) 禁用 CPU(处理器) 缓存、每次读写物理主存
    可见性是 happens-before(先行发生关系) 语义保证
  • happens-before(先行发生关系) 就是时间上先发生
    是「结果对后者可见」
  • 主内存就是物理内存条
    是规范层面的抽象
  • JDK 17/21 上锁升级要经过偏向锁
    偏向锁已默认关闭 / 移除
  • DCL(双重检查锁定) 有 synchronized(内置同步关键字) 就够了
    第一次检查在锁外,必须加 volatile(可见性关键字)
  • 静态同步方法和实例同步方法互斥
    仅靠方法默认同步时锁对象不同;若内部另有共同锁,须按实际路径判断

出处: JavaGuide《JMM(Java 内存模型) 详解》《Java 并发常见面试题总结(中)》、JDK 21 官方文档(java.util.concurrent 包说明、Object.notify()、java 命令手册)、本机实验记录。逐条引用见 第 3 课、第 4 课;存档在 reference/ 目录。

哪一条读着没把握,就回对应的课重看。