Java 内存模型
并发编程的两个关键问题
- 线程间如何通信?
- 线程间如何同步?
有两种模型解决这两个问题。
- 消息传递并发模型
- 共享内存并发模型
通信 | 同步 | |
---|---|---|
消息传递 | 线程间没有公共状态,通过发送消息来显式通信。 | 发送消息天然同步,因为发送消息总是在接收消息之前,同步是隐式的。 |
共享内存 | 通过写-读内存中的公共状态来隐式通信。 | 必须显式指定某段代码在线程间互斥执行,同步时显式的。 |
Java 使用的是共享内存并发模型
Java 内存模型抽象结构
运行时内存的划分
线程共享数据区 | 线程私有数据区 |
---|---|
方法区 | 虚拟机栈 |
堆 | 本地方法栈 |
程序计数器 |
对于每一个线程来说,栈是私有的,堆是共享的。
栈中变量(局部变量,方法定义参数,异常处理器参数)不在线程中共享,也就不会有内存可见性的问题,也不受内存模型的影响。
内存可见性针对的是共享变量。
堆是共享的,为什么会有内存不可见的问题?
因为现代计算器为了高效,往往会在高速缓存区缓存共享变量,因为cpu访问缓存区比内存速度快的多。
线程间共享变量存在于主内存,每个线程都一个私有的本地内存,存储了共享变量的副本。
线程只能堆共享变量的副本进行读-写操作。
- 共享变量都在主内存
- 每个线程都保存了一份共享变量的副本
- 如果线程A与线程B之间要通信,要经历下面的步骤
- 线程A将本地内存A更新的副本刷新到主内存中
- 线程B从主内存读取线程A更新的共享变量
线程A无法直接访问线程B的本地内存。
线程B并不是直接读取主内存的值,而是在本地内存B找到这个共享变量,发现已经被更新了,然后本地内存B去主内存读取最新的共享变量的值,并拷贝的本地内存B,然后线程B从本地内存B获取到值。
如何直到共享变量被其他线程更新了呢?
Java 内存模型( JMM )通过控制主内存与各线程本地内存的交互,来提供内存可见性保证。