Java内存模型
3.1Java内存模型基础
3.1.1并发编程模型的两个关键问题
- 线程之间如何通信
- 线程之间如何同步
Java的并发采用的共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对程序员完全透明。
如果编写多线程程序的java程序员不理解隐式进行的线程之间通信的工作机制,很可能会遇到各种奇怪的内存可见性问题。
3.1.2Java内存模型的抽象结构
在Java中,所有实例域、静态域和数组元素都存在堆内存中,堆内存在线程之间共享。 局部变量,方法定义参数,异常处理参数不会在线程之间共享,它们不会有内存可见性问题,也不受内存 模型的影响。
Java线程之间的通信由Java内存模型(JMM)控制,JMM决定一个线程对共享变量的写入何时对另一个线程可见。 线程之间的共享变量存储在主内存中,每个线程都有一个私有的本地内存(Local Memory),本地内存中存储了该线程 以读/写共享变量的副本。 本地内存是JMM的一个抽象概念,并不是真实存在,它涵盖了缓存、写缓存冲区、寄存器以及其他的硬件和 编译优化。
从图3-1来看,如果线程A与线程B之间要通信的话,必须要经历下面2个步骤。 1)线程A把本地内存A中更新过的共享变量刷新到主内存中去。 2)线程B到主内存中去读取线程A之前已更新过的共享变量。
3.1.3 从源代码到指令序列的重排序
在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。
JMM属于语言级的内存模型,它确保在不同的编译器和不同的处理器平台之上,通过禁 止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。
3.1.4 并发编程模型的分类
现代的处理器使用写缓冲区临时保存向内存写入的数据。写缓冲区可以保证指令流水线 持续运行,它可以避免由于处理器停顿下来等待向内存写入数据而产生的延迟。同时,通过以 批处理的方式刷新写缓冲区,以及合并写缓冲区中对同一内存地址的多次写,减少对内存总 线的占用。虽然写缓冲区有这么多好处,但每个处理器上的写缓冲区,仅仅对它所在的处理器 可见。这个特性会对内存操作的执行顺序产生重要的影响:处理器对内存的读/写操作的执行 顺序,不一定与内存实际发生的读/写操作顺序一致!
3.1.5 happens-before简介
与程序员密切相关的happens-before规则如下。
- 程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
- 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
- volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的 读。
- 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。