第12章 Java内存模型与线程
12.1 概述
介绍虚拟机如何实现多线程,多线程之间因为共享数据而致使的一系列问题及解决方案缓存
12.2 硬件效率与一致性
介绍Java虚拟机内存模型前,先了解下物理机的并发问题。安全
- 硬件效率问题。计算机处理任务除了处理器计算外,还有内存交互,即读写数据。而存储设备与处理机运行速度相差几个数量级,为此引入了读写速度尽量接近处理器的高速缓存Cache。处理器读写缓存数据,缓存将数据同步到内存。
- 缓存一致性问题。在共享内存多核系统中,每一个处理器都有本身的高速缓存,又共享同一主内存。为了解决一致性问题,处理器访问高速缓存时,须要遵循一些协议,好比MSI,MESI,MISI,Synapse,Dragon Protocol等。
- 代码乱序执行优化问题。处理器为了提升运算效率,会出现不按顺序执行的状况,但单线程下,处理器会保证执行结果与顺序执行结果一致。而多线程的状况下,没法保证多个任务都按照顺序执行。
Java虚拟机有本身的内存模型,也会有与物理机类型的问题。markdown

12.3 Java内存模型
12.3.1 概述
Java内存模型规定:因此变量都存储在主内存(Main Memory)中,线程有本身的工做内存,工做内存保存变量在主内存副本。线程对变量的读写只能再工做内存(Working Memory)中,线程间共享变量须要经过主内存完成。
JVM内存模型的执行处理将围绕解决两个问题展开:
复制代码
- 工做内存数据一致性
- 指令重排序优化,编译期重排序和运行期重排序。

12.3.2 内存交互操做
主内存与工做内存的交互协议定义以下操做,Java虚拟机必须保证这些操做是原子性的。多线程
- lock,做用于主内存变量,把变量标识为线程独占状态,使其余线程没法lock
- unlock,做用于主内存变量,解除线程独占状态
- read,做用于主内存变量,把变量值传输到工做内存中,一边随后的load使用
- load,做用于工做内存变量,把read的变量值放入工做内存的变量副本中。
- use,做用于工做内存变量,变量值传递给执行引擎
- assign,做用于工做内存变量,执行引擎赋值给工做内存中的变量
- store,做用于工做内存变量,变量值传输到主内存,以便后续write使用
- write,做用于主内存变量,把store的变量值放入主内存变量中。
若是要把变量从主内存拷贝到工做内存,必须顺序执行 read和load,但不要求必定连续。 若是要把变量从工做内存同步到主内存,必须顺序执行 store和write,但不要求必定连续。并发
12.3.3 内存模型运行规则
1.内存交互基本操做的3个特性
Java内存模型是围绕着在并发过程当中如何处理这3个特性来创建的,归根结底是为了实现共享变量在多个工做内存中的一致性,以及并发时,程序能如期运行。app
- 原子性(Atomicity),即一个操做或者多个操做,要么不执行,要么所有执行且执行过程不会被打断
- 可见性(Visibility),当多个线程访问同一个变量时,一个线程改变了变量值,其余线程要能当即看到修改过的值。线程经过共享主内存实现可见性。
- 有序性(Ordering),线程内指令串行(as-if-serial),线程间,对于同步(synchrinized)代码以及volatile字段的操做须要维持相对有序
2.先行发生原则
happens-before优化
- 程序次序规则
- 管程锁定规则
- volatile变量规则
- 线程启动规则
- 线程终止规则
- 线程中断规则
- 对象终结规则
- 传递性
3.内存屏障
内存屏障是被插入到两个CPU指令之间的一种指令,用来禁止处理器指令发生指令重排序。spa
12.3.4 volatile型变量
volatile主要有下面两种语义线程
语义1 保证可见性
保证了不一样线程对该volatile型变量操做的内存可见性,但不等同于并发操做的安全性code
- 线程写volatile变量的过程assign-store-write必须连续出现:
- 改变工做内存中volatile变量副本的值
- 将改变的副本值刷新到主内存中
- 线程读volatile变量的过程read-load-use必须连续出现:
- 从主内存读取volatile变量值并存入工做线程副本
- 从工做内存读取变量副本
语义2 禁止指令重排序
volatile型变量使用场景总结起来就是"一次写入,处处读取",某个线程负责更新变量,其余线程只读取变量,并根据变量新值执行相应逻辑,例如状态标志位更新,观察者模型变量值发布