深刻理解Java虚拟机读书笔记-第12章 Java内存模型与线程

第12章 Java内存模型与线程

12.1 概述

介绍虚拟机如何实现多线程,多线程之间因为共享数据而致使的一系列问题及解决方案缓存

12.2 硬件效率与一致性

介绍Java虚拟机内存模型前,先了解下物理机的并发问题。安全

  • 硬件效率问题。计算机处理任务除了处理器计算外,还有内存交互,即读写数据。而存储设备与处理机运行速度相差几个数量级,为此引入了读写速度尽量接近处理器的高速缓存Cache。处理器读写缓存数据,缓存将数据同步到内存。
  • 缓存一致性问题。在共享内存多核系统中,每一个处理器都有本身的高速缓存,又共享同一主内存。为了解决一致性问题,处理器访问高速缓存时,须要遵循一些协议,好比MSI,MESI,MISI,Synapse,Dragon Protocol等。
  • 代码乱序执行优化问题。处理器为了提升运算效率,会出现不按顺序执行的状况,但单线程下,处理器会保证执行结果与顺序执行结果一致。而多线程的状况下,没法保证多个任务都按照顺序执行。

Java虚拟机有本身的内存模型,也会有与物理机类型的问题。markdown

截屏2020-09-01上午9.38.11.png

12.3 Java内存模型

12.3.1 概述

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

截屏2020-09-01上午9.37.19.png

12.3.2 内存交互操做

主内存与工做内存的交互协议定义以下操做,Java虚拟机必须保证这些操做是原子性的。多线程

  • lock,做用于主内存变量,把变量标识为线程独占状态,使其余线程没法lock
  • unlock,做用于主内存变量,解除线程独占状态
  • read,做用于主内存变量,把变量值传输到工做内存中,一边随后的load使用
  • load,做用于工做内存变量,把read的变量值放入工做内存的变量副本中。
  • use,做用于工做内存变量,变量值传递给执行引擎
  • assign,做用于工做内存变量,执行引擎赋值给工做内存中的变量
  • store,做用于工做内存变量,变量值传输到主内存,以便后续write使用
  • write,做用于主内存变量,把store的变量值放入主内存变量中。

截屏2020-09-01下午1.31.23.png 若是要把变量从主内存拷贝到工做内存,必须顺序执行 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 禁止指令重排序截屏2020-09-01下午2.22.56.png

volatile型变量使用场景总结起来就是"一次写入,处处读取",某个线程负责更新变量,其余线程只读取变量,并根据变量新值执行相应逻辑,例如状态标志位更新,观察者模型变量值发布