《深入理解Java虚拟机》第12章笔记
2026/8/24大约 3 分钟
《深入理解Java虚拟机》第12章:Java 内存模型与线程
本章是并发编程的理论基石:主内存/工作内存、volatile 语义、先行发生原则与线程实现。
一、主内存与工作内存
- Java 内存模型规定所有变量存储于主内存(Main Memory)
- 每条线程拥有自己的工作内存(Working Memory),保存变量主内存副本
- 线程对变量的所有操作必须在工作内存进行,不能直接读写主内存
- 线程间变量传递需通过主内存完成
二、内存间交互操作(八种)
| 操作 | 作用对象 | 含义 |
|---|---|---|
lock | 主内存 | 标识变量为线程独占 |
unlock | 主内存 | 释放锁定状态 |
read | 主内存 | 把变量值从主内存传输到工作内存 |
load | 工作内存 | 把 read 得到的值放入变量副本 |
use | 工作内存 | 把变量值传给执行引擎 |
assign | 工作内存 | 把执行引擎的值赋给变量 |
store | 工作内存 | 把变量值传送到主内存 |
write | 主内存 | 把 store 得到的值放入主内存变量 |
核心规则:read 与 load、store 与 write 必须按顺序执行(不要求连续);变量只能在主内存"诞生";lock 可重入但 unlock 次数需匹配;unlock 前必须先同步回主内存。
三、volatile 的特殊规则
- 可见性:volatile 变量修改对其他线程立即可见
- 不保证原子性:仅当满足以下两条才可安全使用(否则需加锁):
- 运算结果不依赖变量当前值,或只有单线程修改
- 变量不与其他状态变量共同参与不变约束
- 禁止重排序:写操作会插入内存屏障,性能略低于普通变量,但整体优于锁
long 与 double 的特殊性
- 允许虚拟机将非 volatile 的 64 位读写拆分为两次 32 位操作(不保证原子性)
四、先行发生原则(happens-before)
| 规则 | 内容 |
|---|---|
| 程序次序 | 线程内按控制流顺序,前者先行发生于后者 |
| 管程锁定 | unlock 先行发生于对同一锁的后续 lock |
| volatile 变量 | 写先行发生于对同一变量的后续读 |
| 线程启动 | start() 先行发生于线程内所有动作 |
| 线程终止 | 线程内所有操作先行发生于终止检测(join()/isAlive()) |
| 线程中断 | interrupt() 先行发生于检测到中断(interrupted()) |
| 对象终结 | 构造完成先行发生于 finalize() |
| 传递性 | A 先行于 B、B 先行于 C ⇒ A 先行于 C |
五、线程的实现与调度
实现方式
- 内核线程实现(1:1)
- 用户线程实现(1:N)
- 混合实现(N:M)
调度方式
- 协同式:线程自己控制执行时间,主动让出;实现简单、无同步问题,但执行时间不可控
- 抢占式:系统分配执行时间,线程无法自行获取;Java 采用抢占式
六、线程状态转换
| 状态 | 说明 |
|---|---|
| 新建(New) | 创建后尚未启动 |
| 运行(Runnable) | 含 Running 与 Ready,可能正在执行或等待 CPU |
| 无限期等待(Waiting) | 需被显式唤醒:wait()、join()、LockSupport.park() |
| 限期等待(Timed Waiting) | 到时自动唤醒:sleep()、带超时的 wait()/join()、parkNanos() |
| 阻塞(Blocked) | 等待获取排它锁,锁释放时进入 |
| 结束(Terminated) | 已终止 |