转载

Java内存模型

理解Java内存模型的概念及原理,对于使用Java语言开发的工作者来说至关重要,对实际项目开发也有促进作用,也时常作为面试题出现在各大互联网公司的面试环节。本篇简要介绍了Java内存模型关键部分内容,结合原理图具体说明。深入理解Java内存模型细节推荐阅读《深入理解Java虚拟机》第12章,本文主要原理内容说明也来自于此。

硬件的效率与一致性

  • 主内存

简单来说是一片共享的内存区域。计算机的处理器总是并发的执行多任务,在执行任务期间处理器会与主内存进行交互,如读取数据、存储运算结果等。

  • 高速缓存

内存与处理器之间的缓冲。由于计算机的存储设备(IO操作)与处理器的运算速度有几个数量级的差距,所以现代计算机系统引入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算速度能快速进行,当运算结束背后再从缓存同步回主内存中,这样处理器旧无需等待缓慢的内存读写了。

  • 缓存一致性协议

当多个处理器的运算任务都涉及同一主内存时,可能出现各自的缓存数据不一致,而最终同步到主内存以哪个缓存数据为准。缓存一致性协议主要是约定了读写数据操作的协议,根据协议最终能保证数据一致性。

处理器、高速缓存、主内存间的交互关系:

Java内存模型

Java内存模型

Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以相互类比,但此处仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存保存了该线程使用到的变量的主内存拷贝副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的关系图:

Java内存模型
  • note:假设线程中访问一个10M的对象,也会把10M的内存复制一份拷贝出来吗?

事实上并不会如此,这个对象的引用、对象中某个在线程访问到的字段是有可能存在拷贝的,但不会有虚拟机实现成把整个对象拷贝一次。

内存间交互操作

Java内存模型定义的8种操作:(均是原子的吧,不可再分的,但是对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许有例外)

  • lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
  • unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
  • read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用。
  • load(载入):作用于工作内存的变量,它把通过read操作从主内存中得到的变量值放入工作内存的变量副本中。
  • use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
  • assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
  • store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作使用。
  • write(写入):作用于主内存的变量,它把通过store操作从工作内存中得到的变量的值放入主内存的变量中。

通过上面的内存交互规则,如果要把一个变量从主内存复制到工作内存, 就需要顺序的执行read和load操作,而如果要把一个变量从工作内存同步回主内存,则需要顺序执行store和write操作,注意是顺序执行,而不是连续执行,也就是两个操作之间可以插入其他操作。除此之外,Java内存模型对这8中操作进行操作的其他约束:

  • 不允许read和load、store和write操作之一单独出现。
  • 不允许线程丢弃它的最近的assign操作,即变量在工作内存中改变之后,必须同步回主内存。
  • 不允许线程把没有经过assign操作的变量,同步回主内存。
  • 一个新的变量只能在主内存中诞生,即对一个变量进行use、store操作之前,必须先执行过load、assign操作。
  • 一个变量在同一时刻只能被一条线程执行lock操作,一旦lock成功,可以被同一线程重复lock多次,多次执行lock之后,只有执行相同次数的unlock操作,变量才会被解锁。
  • 对一个变量执行lock操作,将会清空工作内存中该变量的值,所以在执行引擎使用这个变量前,需要重新执行load或assign操作对其进行初始化。
  • 对一个变量执行unlock操作之前,必须先把该变量同步回主内存(执行store、write操作)。
  • 如果一个变量事先没有被lock操作锁定,那就不允许对它执行unlock操作,也不允许unlock一个被其他线程lock的变量。

关于内存交互操作的流程简图:

Java内存模型

volatile变量

volatile变量具备两种特性:

  • 保证变量对所有线程的可见性,这里的“可见性”是指当一个线程修改了这个变量值,新值对于其他线程来说是可以立即得到的。

  • 禁止指令重排序优化

关于volatile变量,除了理解可见性以及指令重排,理解原子性有序性以及volatile对于long和dounble型变量的特殊规则,先行发生原则也至关重要,由于篇幅关系本文不再深入介绍。

原文  https://juejin.im/post/5d260a7af265da1b5d57d4a8
正文到此结束
Loading...