JMM Java 内存模型
-
定义程序中各个变量的访问规则,即变量是如何存入内存、如何从内存取出的;
变量包括:实例字段、静态字段、构成数组对象的元素,但不包括线程私有的局部变量和方法参数(不会被共享,也就不存在竞争问题)
-
工作内存与主内存
- 所有的变量都存在主内存中
- 每条线程有自己独立的、相互隔离的工作内存,保存了被该线程使用的变量的主内存副本拷贝;线程对变量的所有操作都必须在工作内存进行,不能直接读写主内存的变量
- 线程间变量值的传递均需要通过主内存来完成
-
内存间交互操作
- 操作规则:
- 不允许
read
和load
、store
和write
操作之一单独出现;从主内存读取的变量必须要装载到工作内存、工作内存发起存储的变量必须要写入主内存 - 不允许一个线程丢弃最近的
assign
操作;变量改变了必须同步到主内存 - 不允许一个线程在未发生任何
assign
操作时,把数据从线程的工作内存同步到主内存 - 新变量只能在主内存中产生,不允许在工作内存中直接使用一个未
load
或assign
的变量 - 一个变量同一时刻只允许一条线程对其进行
lock
操作 - 对一个变量执行
lock
操作,会清空工作内存中此变量的值;使用这个变量前,需重新load
或assign
初始化变量的值 - 变量没有
lock
则不允许unlock
- 对一个变量执行
unlock
操作前,必须先把次变量同步回主内存(store
和write
操作)
- 不允许
- volatile 变量的特殊规则
- 对所有线程的可见性
- 对 volatile 变量所有的写操作都能立即反应到其他线程之中(每次使用之前都要先刷新)
- 线程内有序性,禁止指令重排序
- 线程内表现为串行的语义 (Within-Thread As-If-Serial Semantics)
- 在本地代码中插入许多内存屏障指令来保证处理器不发生乱序执行
- 只有原子操作才是并发安全的
-
i++;
典型场景
-
- 对所有线程的可见性
- long 和 double 型变量的特殊规则
- 运行虚拟机将没有被 volatile 修饰的 64 位数据的读写操作划分为两次 32 位的操作来进行;非原子性协定(Nonatomic Treatment of double and long Variables)
- 多线程可能会出现半个变量的情况(非常罕见)
- 实际开发中多数虚拟机都把 64 位数据的读写操作作为原子操作对待
- 操作规则:
关键字 | 原子性 | 可见性 | 有序性 |
---|---|---|---|
synchronized | √ | √ | √ |
volatile | × | √ | √ |
final | - | √ | - |
参考
- wiki
- specs
- 深入理解 Java 虚拟机 第 12 章 Java 内存模型与线程