理解Java内存模型的概念及原理,对于使用Java语言开发的工作者来说至关重要,对实际项目开发也有促进作用,也时常作为面试题出现在各大互联网公司的面试环节。本篇简要介绍了Java内存模型关键部分内容,结合原理图具体说明。深入理解Java内存模型细节推荐阅读《深入理解Java虚拟机》第12章,本文主要原理内容说明也来自于此。
简单来说是一片共享的内存区域。计算机的处理器总是并发的执行多任务,在执行任务期间处理器会与主内存进行交互,如读取数据、存储运算结果等。
内存与处理器之间的缓冲。由于计算机的存储设备(IO操作)与处理器的运算速度有几个数量级的差距,所以现代计算机系统引入一层读写速度尽可能接近处理器运算速度的高速缓存(Cache)来作为内存与处理器之间的缓冲:将运算需要使用到的数据复制到缓存中,让运算速度能快速进行,当运算结束背后再从缓存同步回主内存中,这样处理器旧无需等待缓慢的内存读写了。
当多个处理器的运算任务都涉及同一主内存时,可能出现各自的缓存数据不一致,而最终同步到主内存以哪个缓存数据为准。缓存一致性协议主要是约定了读写数据操作的协议,根据协议最终能保证数据一致性。
处理器、高速缓存、主内存间的交互关系:
Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以相互类比,但此处仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存保存了该线程使用到的变量的主内存拷贝副本,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,线程、主内存、工作内存三者的关系图:
事实上并不会如此,这个对象的引用、对象中某个在线程访问到的字段是有可能存在拷贝的,但不会有虚拟机实现成把整个对象拷贝一次。
Java内存模型定义的8种操作:(均是原子的吧,不可再分的,但是对于double和long类型的变量来说,load、store、read和write操作在某些平台上允许有例外)
通过上面的内存交互规则,如果要把一个变量从主内存复制到工作内存, 就需要顺序的执行read和load操作,而如果要把一个变量从工作内存同步回主内存,则需要顺序执行store和write操作,注意是顺序执行,而不是连续执行,也就是两个操作之间可以插入其他操作。除此之外,Java内存模型对这8中操作进行操作的其他约束:
关于内存交互操作的流程简图:
volatile变量具备两种特性:
保证变量对所有线程的可见性,这里的“可见性”是指当一个线程修改了这个变量值,新值对于其他线程来说是可以立即得到的。
禁止指令重排序优化
关于volatile变量,除了理解可见性以及指令重排,理解原子性有序性以及volatile对于long和dounble型变量的特殊规则,先行发生原则也至关重要,由于篇幅关系本文不再深入介绍。