Java内存模型

缓存一致性

在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一块内存。当多个处理器的运算任务都涉及到同一块内存区域时,将可能导致各自的缓存数据不一致。为了解决一致性的问题,需要各个处理器访问缓存是都遵守一些协议。
在这里插入图片描述

Java内存模型

通过Java内存模型来屏蔽掉各种硬件和操作系统的内存访问差异,已实现让Java程序在各个平台下都能达到一致的内存访问效果。
主要目标是定义程序中各个变量的访问规则,即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节。此处的变量(Variables)与java编程中所说的变量有所区别,它包括实例字段静态字段构成数组对象的元素,但不包括局部变量与方法参数,因为后者是线程私有的,不会被共享,自然不存在竞争问题。
主内存(虚拟机内存的一部分):所有的变量(实例字段、静态字段、构成数组对象的元素)
工作内存(虚拟机栈的一部分):线程不能直接读写主内存中的变量,需要拷贝主内存的副本进行操作,线程之间无法互相共享工作内存中的数据,需要通过主内存进行通信
在这里插入图片描述

内存间交互操作

  • lock(锁定):作用于主内存变量,把一个变量标识为一条线程独占状态
  • unlock(解锁):作用于主内存变量,把一个锁定的变量释放出来,只有被释放的变量才能被其它引用使用
  • read:作用于主内存,把一个变量的值从主内存传递到工作内存
  • load:作用于工作内存,将read读取的变量值放入工作内存的副本中
  • use:作用于工作内存,遇到执行执行,将工作内存中的副本值传递到执行引擎
  • assign:作用于工作内存,将执行引擎中的值赋值给工作内存中的副本
  • store:作用于工作内存,将工作内存中的副本值传递到主内存
  • write:作用于主内存,将store传递的副本值放入对应的主内存变量中

在这里插入图片描述
内存操作八条规则

  1. 不允许read、load、store和write操作之一单独出现,即由读操作开始,必须有写操作结束
  2. 不允许丢弃assign操作,副本在工作内存中改变之后必须被写完主内存
  3. 新变量只能在内存中诞生,不允许在工作内存中直接使用一个未被初始化的变量(load、assign),即对一个变量实施操作之前,必须先执行过assign和load操作
  4. 一个变量仅允许一条线程对其进行lock操作,但是拥有该变量锁的线程可以对该变量进行多次lock操作,调用多次lock操作时,只有调用了相同的unLock操作,对象才有可能被其它线程使用
  5. 如果一个变量被执行lock操作,那将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行read和load操作
  6. 若果一个变量事先没有被lock锁定,不允许对变量执行unlock操作,也不允许区unlock一个被其它线程锁定的对象
  7. 一个变量执行unlock之前,必须先把此变量同步回主内存中
  8. 不允许一个线程无原因的(没有发生过任何assign操作)把数据从线程的工作内存同步回主内存

Volatile型变量的特殊规整

**可见性保证:**工作内存中,每次对volatile变量使用之前都会进行刷新,修改之后也会马上对内存进行修改
**禁止指令重排序优化:**在经过编译之后,无前后依赖关系的操作指令可能会出现执行顺序与源程序中定义顺序不一致的情况(可将无依赖关系的计算任务交由不同电路结构处理);实现机制,在volatile变量使用处插入一个屏障,使得屏障之后的操作指令无法在屏障之前的操作指令执行之前执行
**原子性:**volatile变量无法保证执行引擎中的一致性,因为执行引擎中可能存在非原子性的操作,导致执行引擎中的操作数不是最新的情况出现

Volatile消除原子性操作适用条件:

  • 运行结果并不依赖变量的当前值,或者能够确保只有单一线程修改变量的值
  • 变量不需要与其它状态变量共同参与不变约束

内存屏障适用范围:

  • 只有一个CPU访问内存时,并不需要内存屏障;
  • 但如果有两个或者多个CPU访问同一块内存时,且其中有一个在观测另一个,就需要内存屏障来保证一致性

double、long型变量的特殊规则

Java内存模型要求lock、unlock、read、load、assign、use、store、write这8个基本操作都具有原子性
long、double非原子性协定:允许虚拟机将没有声明为volatile的long、double的64位数据读写操作分成两次32位数据的读写操作
非原子协定问题:多线程情况下,可能出现写一半的同时,另一条线程开始读的情况
实际情况:目前的商用虚拟机都对64位数据的两次32位读写操作实现了原子性操作

原子性、可见性与有序性

  • 原子性:Java内存模型直接保证原子性的操作包括基本类型的8中基本操作(除非原子协定之外),同时synchronized之间也具有原子性
  • 可见性:至当前线程修改了变量的值,其它线程可以立刻知道(volatile、final、synchronized可实现可见性)。
    实现方式:每次使用变量之前,对变量进行一次刷新,每次对变量修改之后保证立刻写会内存。
  • 有序性:本线程上,所有的操作都是有序的;但是在一个线程中观察另一个线程,所有的操作都是无序的。volatile与synchronized可以实现有序性。
    volatile通过禁止指令重排实现,synchronized则是由“一个变量在同一时刻只允许一条线程对其进行lock操作”,这条规则决定了持有同一个锁的两个同步块只能串行地进入。

先行发生原则

  • 程序次序规则:一个线程内,按照程序代码顺序(控制流顺序执行)
  • 管程锁定规则:unlock操作先行于后面对同一个锁的lock操作
  • volatile变量规则:volatile变量的写操作优先于读操作
  • 线程启动规则:Thread的start方法先行于此线程的每一个动作
  • 线程终止规则:线程中所有操作都优先于终止操作。通过Thread.join()方法结束、Thread.isAlive()的返回值等手段检测到线程已经终止
  • 线程中断规则:对线程interupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生
  • 对象终结规则:一个对象初始化完成(构造函数执行结束)先行于finalize()方法开始
  • 传递性:A先行于B,B先行于C,可得出A先行于C
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章