深入理解Java虚拟机:高效并发之Java内存模型
计算机系统并发执行
高速缓存(Cache):作为内存与处理器之间的缓冲;
【深入理解Java虚拟机:高效并发之Java内存模型】将运算需要使用的数据复制到缓存中 , 让运算能快速进行 , 当运算结束后在冲缓存同步回内存之中 , 这样处理器就无须等待缓慢的内存读写了 。
缓存一致性(CacheConherence)
当多处理器的运算任务都涉及同一块主内存区域时 , 需要各个处理器访问缓存时都遵循一定协议 , 在读写时要根据协议来进行操作(MSI、MESI、MOSI、Synapse、Firefly、DragonProtocol) 。
为了使处理器内部的运算单元能尽量被充分利用 , 处理器可能会对输入代码进行乱序执行优化 , 处理器会在计算之后将乱序执行的结果重组 , 保证该结果与顺序执行的结果是一直的 , 但并不保证程序中各个语句计算的先后顺序与输入代码中的顺序一直 。
文章图片
处理器、高速缓存、主内存间的交互关系
Java内存模型
Java内存模型(JavaMemoryModel , JMM):来屏蔽各种硬件和操作系统的内存访问差异 , 以实现让Java程序在各种平台下都能达到一致的内存访问效果 。
文章图片
线程、主内存、工作内存三者的交互关系
主内存:对应于Java堆中的对象实例数据部分 。
工作内存:对应于虚拟机栈中的部分区域 。
内存间交互操作
主内存与工作内存之间具体的交互协议
lock(锁定):作用于主内的变量 , 它把一个变量标识为一条线程独占的状态 。
unlock(解锁):作用于主内存的变量 , 它把一个处于锁定状态的变量释放出来 , 释放后的变量才可以被其他线程锁定 。
read(读取):作用于主内存的变量 , 它把一个变量的值从主内存传输到线程的工作内存中 , 以便随后的load动作使用 。
load(载入):作用于工作内存的变量 , 它把read操作从主内存中得到的变量值放入工作内存的变量副本中 。
use(使用):作用于工作内存的变量 , 它把工作内存中一个变量的值传递给执行引擎 , 每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作 。
assign(赋值):作用于工作内存的变量 , 它把一个执行引擎接受到的值赋给工作内存的变量 , 每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作 。
store(存储):作用于工作内存的变量 , 它把工作内存中一个变量的值传送到主内存中 , 以便随后的write操作使用 。
write(写入):作用于主内存的变量 , 它把store操作从工作内存中得到的变量的值放入主内存的变量中 。
执行基本操作时满足的规则:
不允许read和load、store和write操作之一单独出现 , 即不允许一个变量从主内存读取了但工作内存不接受 , 或者工作内存发起回写了但主内存不接受的情况出现 。
不允许一个线程丢弃它最近的assign操作 , 即变量在工作内存中改变了之后必须把该变化同步回主内存 。
不允许一个线程没有发生过任何assigin操作把数据从线程的工作内存同步回主内存中 。
一个新的变量只能在主内存中"诞生" , 不允许在工作内存中直接使用一个未被初始化(load或assign)的变量 , 就是对一个变量实施use、store操作之前 , 必须先执行assigin和load操作 。
一个变量在同一时刻只允许一条线程对其进行lock操作 , 但lock操作可以被同一线程重复执行多次 , 多次执行lock后 , 只有执行相同次数的unlock操作 , 变量才会被解锁 。
如果对一个变量事先没有被lock操作锁定 , 那就不允许对它执行unlock操作 , 也不允许去unlock一个被其他线程锁定的变量 。
对一个变量unlock操作之前 , 必须先把此变量同步回主内存中(执行store、write操作) 。
volatile
特性
保证此变量对所有线程的可见性 , 但对于运算不是原子操作(当一条线程修改了一个变量的值 , 新值对于其他线程来说是可以立即得知的) 。
文章图片
由于volatile变量只能保证可见性 , 在不符合规则的运算场景中 , 仍然需要枷锁来保证原子性
运算结果不依赖变量的当前值 , 或者能够保证只有单一的线程修改变量的值 。
变量不需要与其他的状态变量共同参与不变约束 。
禁止指令重排序优化
文章图片
原子性、可见性与有序性
原子性:基本数据类型的访问、读写都是具备原子性的;大范围的原子性保证(synchronized)
可见性:当一个线程修改了共享变量的值时 , 其他线程能够立即得知这个修改 。 (synchronized、final)
有序性:volatile、synchronized两个关键字保证线程之间的有序性;volatile禁止了指令重排序 , synchronized则是一个变量在同一个时刻只允许一条线程对其进行了lock操作 。
Happens-before原则
程序次序规则:在一个线程内 , 按照控制流顺序 , 书写在前面的操作先行发生于书写在后面的操作 。
管程锁定规则:一个unlock操作先行生发于后面对同一个锁的lock操作 。
volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作 。
线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作 。
线程终止规则:线程中的所有操作都先行发生于对此线程的终止检查 , 通过Thread::join()方法是否结束、Thread::isAlive()的返回值等检测线程是否已经终止执行 。
对象终结规则:一个对象的初始化完成先行发生于它的finalize()方法的开发 。
传递性:如果操作A先行发生于操作B , 操作B先行发生于操作C , 那么操作A先行发生于操作C 。
推荐阅读
- 王毅王毅感谢给中国理解关心帮助的各国人民
- 延安市交警支队副调研员杨军深入安塞大队督导检查第二季度道路交通安全“保平安、促发展”行动
- 学院党委书记一行深入公共基础部指导返校复学工作
- 加强宣传材料合规宣导,深入推进行业规范建设
- 宋佳真大胆,穿西装服配碎花裙,网友:土味时尚难理解 ?
- 聚焦 | 市检察院党组书记、检察长张忠明深入包联责任区开展调研
- 霸州市检察院深入堂二里镇开展法治宣传活动
- 都兰交警深入辖区客运公司开展交通安全隐患检查工作
- 淅川县西簧乡派出所民警深入西簧初中检查安全工作
- 喝红茶能养胃,喝绿茶却伤胃?事实是什么?原来很多人没理解对