Java内存模型
Java内存模型
内存模型限制的是共享变量,也就是存储在堆内存中的变量
所有的
实例变量
、静态变量
和数组元素
都存储在堆内存之中。而
方法参数
、异常处理参数
这些局部变量存储在方法栈帧之中,因此不会在线程之间共享,不会受到内存模型影响,也不存在内存可见性问题。
线程之间的通讯方式
- 共享内存: 在共享的内存模型里,多线程之间共享程序的公共状态,通过读-写内存的方式来进行隐式通讯。
- 消息传递
从抽象角度看
-
JMM 其实是定义了线程和主内存之间的关系
- 多个线程之间的共享变量存储在主内存之中
- 同时每个线程都有一个自己私有的本地内存
- 本地内存中存储着该线程读或写共享变量的副本
- 注意:本地内存是 JMM 定义的抽象概念,实际上并不存在
-
两个线程之间的通信(
共享变量状态变更
)会发生如下操作- 线程 A 把在本地内存更新后的共享变量副本的值,刷新到主内存中。
- 线程 B 在使用到该共享变量时,到主内存中去读取线程 A 更新后的共享变量的值,并更新线程 B 本地内存的值。
-
JMM 本质上是在硬件(处理器)内存模型之上又做了一层抽象,使得应用开发人员只需要了解 JMM 就可以编写出正确的并发代码,而无需过多了解硬件层面的内存模型。
为什么需要 Java 内存模型
解决
可见性
和有序性
问题
有序性:指代码实际的执行顺序和代码定义的顺序一致
JMM 对在
正确同步的程序
做了顺序一致性的保证,也就是程序的执行结果和该程序在顺序一致性内存模型中的执行结果相同。
可见性:JMM 正是通过Happens-Before规则来保证跨线程的内存可见性
Happens-Before
: 前面一个对共享变量的操作结果对该变量的后续操作是可见的
- 程序顺序规则:一个线程中的每个操作,
Happens-Before
该线程中的任意后续操作- 监视器锁规则:对一个锁的解锁操作,
Happens-Before
于后面对这个锁的加锁操作- volatile 规则: volatile变量的写先于读发生,这保证了volatile变量的可见性
- 传递性规则:如果操作 A
Happens-Before
于操作 B,并且操作 BHappens-Before
于操作 C,则操作 AHappens-Before
于操作 C- start() 规则:如果一个线程 A 执行操作
threadB.start()
启动线程 B,那么线程 A 的start()
操作Happens-Before
于线程 B 的任意操作- join() 规则:如果线程 A 执行操作
threadB.join()
并成功返回,那么线程 B 中的任意操作Happens-Before
于线程 A 从threadB.join()
操作成功返回- 线程的中断(interrupt())先于被中断线程的代码
则 只要不改变单线程和正确同步的多线程的执行结果,编译器和处理器随便怎么优化都可以
- Happens-Before 是多线程下的
sa-if-serial
- sa-if-serial :不管怎么重排序,在单线程下的执行结果不能改变