Java并发编程之Java内存模型
1、什么是java的内存模型
java内存模型简称jmm(java memory model
),jmm是和多线程并发相关的一组规范。各个jvm实现都要遵循这个jmm规范。才能保证java代码在不同虚拟机顺利运行。因此,jmm 与处理器、缓存、并发、编译器有关。它解决了cpu 多级缓存、处理器优化、指令重排等导致的结果不可预期的问题。
2、为什么需要java内存模型
程序的运行结果依赖于处理器,而不同的处理器规则都不一样,不同处理器差异是很大的,所以同段代码在处理器a运行正常,搬到处理器b运行结果是不一样的,所以为了兼容这种差异,推出了java内存模型规范,jmm是一个规范标准,jmm保证了不同处理器的处理结果一致,同时也保证不同编译器、jvm等等的一致性。所以就保证了java语言“书写一次、到处运行”
3、java内存模型及操作规范
1.共享变量都是放在主内存中的
2.每个线程都有自己的工作内存,线程只可操作自己的工作内存
3.线程要操作共享变量,需要从主内存中读取到工作内存,改变值之后要从工作内存同步到主内存
4、java内存模型规定的原子操作
java内存模型的同步交换协议,规定了8种原子操作
原子操作:不可被中断的一个或一系列操作
-
lock
(锁定):将主内存中的变量锁定,为一个线程所独占 -
unlock
(解锁):将lock加的锁解除,其他的线程有机会访问此变量 -
read
(读取):作用于主内存变量,将主内存中的变量值读取到工作内存 -
load
(加载):作用于工作内存,将read读取到的值保存到工作内存中的变量副本 -
use
(使用):作用于工作内存变量,将值传递给线程的代码执行引擎 -
assign
(赋值):作用于工作内存变量,将执行引擎处理返回的值重新赋值给变量副本 -
store
(存储):作用于工作内存变量,将变量副本的值传送到主内存中 -
write
(写入):作用于主内存变量,将store传送过来的值写入到主内存的共享变量中
java内存模型的同步交互协议,执行上述8种原子操作时必须满足如下规则
不允许read和load,store和write操作之一单独出现。即不允许加载或同步工作到一半。
不允许一个线程丢弃它最近的assign操作,即变量在工作内存中改变之后,必须将数据同步回主内存
不允许一个线程无原因地(无assign操作)将数据从工作内存同步到主内存中。
一个新的变量可能在主内存中诞生。
一个变量在同一个时刻只允许一条线程对其进行lock操作,但lock操作可以被同一条线程重复执行多次,多次lock之后必须要执行相同次数unlock操作,变量才会解锁
如果对一个对象进行lock操作,那么会清空工作内存变量中的值,在执行引擎使用这个变量前,需要重新执行load或assign操作初始变量的值
如果一个对象事先没有被lock,就不允许对其进行unlock操作,也不允许去unlock一个被其他线程锁住的变量。
对一个变量执行unlock操作之前,必须将此变量同步回主内存中(执行store、write)
5、java内存模型同步协议
java内存模型的同步协议,操作规范 将一个变量从主内存复制到工作内存要顺序执行read、load操作;要将变量从工作内存同步回主内存要用store、write操作。只要求顺序执行,不一定是连续执行
图引用网上资料:
6、java内存模型的hb法则
并发编程有三个重要特效:原子行、可见性、有序性
- 原子性:原子性是指一个或者多个操作,要么全部执行且执行过程不会被其它操作打断,要么全部不执行。
- 可见性:可见性是指共享变量对于多个线程都是可见的,也即一个线程修改了变量,其它线程马上就能知道
- 有序性:有序性是指程序的执行顺序按照代码的先后顺便执行
在说jmm的happens-before(hb)法则之前,先说说并发编程的有序性。说到并发线程的有序性,还需要涉及到指令重排序
- 什么是指令重排?
假如我们写一个程序,我们会期待这些语句的实际执行顺便和代码的顺序是一致的,大部分情况是一致的,但实际上,编译器、jvm 或者 cpu 都有可能出于优化等目的,对执行的顺序进行调整,这个就是指令重排序
- 重排序的好处:提高处理速度
代码顺序如图:
指令重排后,a=100; a= a+100会提到一起执行,效率提高
上面的例子,是可以提高执行效率,但是有时候指令重排是会导致问题的,如下代码例子,代码顺序是先初始化content,然后设置标识为true,线程b检测到为true之后,调用content的方法
如果指令重排后,这种情况就会出现没初始化完成,就直接调用conten的方法
所以,指令重排有好处也有坏处,一般可能是cpu、编译器或者是内存会进行指令重排,为了避免指令重排,保证并发编程的有序性,有时候需要使用synchronized等锁或者volatile等等方式避免
1.jmm规定了happens-before(先行发生)原则,来保证很多操作的有序性。
2.当我们代码操作不满足先行发生原则时,则需在编码时使用volatile、synchronized来保证有序性
jmm的hb法则
- 程序顺序规则:每个线程的每个操作都happens-before该线程中任意的后续操作
- 监视器锁规则:一个锁的解除,happens-before于随后对这个锁的加锁
- volatile变量规则:对volatile域的写,happens-before于任意后续对这个volatile域的读
- 线程启动规则:在某个线程对象上调用start()方法happens-before被启动线程中的任意动作
- 线程终止规则:线程中所有操作都先行发生于对此线程的终止检测,如在线程t1中成功执行了t2.join(),则t2中的所有操作对t2可见
- 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
- 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize方法的开始传
- 递性:如果a happens-before于b,且b happens-before 于c,那么a happens-before于c
总结
本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注的更多内容!
上一篇: 酷狗音乐歌单如何取消隐私设置?