1.常规cpu模型
- 增加了缓存的概念,就比如cpu的一级二级缓存等等(速度大于主内存)
2.Java线程内存模型
- Java线程内存模型跟cpu缓存模型类似,是基于CPU缓存模型建立起来的,Java线程内存模型时标准化的,屏蔽了底层不同计算机的区别
- JMM有单独的工作内存,即使主内存变量修改了,其他线程也不一定得到通知,示例如下:
package main; public class Test6 { private static boolean initFlag = false; public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("waiting data……"); while (!initFlag){ } System.out.println("…………………………………………success"); } }).start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Runnable() { @Override public void run() { prepareData(); } }).start(); } private static void prepareData() { System.out.println("preparing data……"); initFlag = true; } } //执行结果//waiting data……//preparing data……//程序不会停止
- 解决方法:加volatile关键字(主内存修改了之后,通知其他线程,从主内存中重新获取),上述代码执行结果如下:
waiting data……preparing data………………………………………………success
3.Java内存模型原子操作
- 所有内存操作都有这几个原子操作进行
- lock:作用于主内存,它把一个变量标记为一条线程独占状态;
- read:作用于主内存,它把变量值从主内存传送到线程的工作内存中,以便随后的load动作使用;
- write:作用于主内存,它把store传送值放到主内存中的变量中。
- unlock:作用于主内存,它将一个处于锁定状态的变量释放出来,释放后的变量才能够被其他线程锁定;
- load:作用于工作内存,它把read操作的值放入工作内存中的变量副本中;
- use:作用于工作内存,它把工作内存中的值传递给执行引擎,每当虚拟机遇到一个需要使用这个变量的指令时候,将会执行这个动作;
- assign:作用于工作内存,它把从执行引擎获取的值赋值给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时候,执行该操作;
- store:作用于工作内存,它把工作内存中的一个变量传送给主内存中,以备随后的write操作使用;
4.上述代码内存模型(未加volatile时)(其中小1处已在主内存,为了美观放在了外面)
image5.JMM缓存不一致问题解决
- 总线加锁:cpu从主内存读取数据到高速缓存,会在总线对这个数据加锁,这样其他cpu没法去读或写这个数据,直到这个cpu使用完数据释放锁之后其他cpu才能读取到该数据(效率低)
- MESI缓存一致性协议:多个cpu从主内存读取同一个数据到各自的高速缓存,当其中某个cpu修改了缓存里的数据,该数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据的变化从而将自己缓存里的数据失效(方法模型如下图)
- 数据修改之后经过总线(store操作)时,通过cpu总线嗅探机制察觉到数据的修改,将其他线程工作内存中的值设为失效
6.Volatile缓存可见性实现原理
- 底层实现主要是通过汇编lock前缀指令(如下图),它会锁定这块内存区域的缓存(缓存行锁定),并写回主内存;
- lock指令开启MESI和总线嗅探机制等,且使cpu执行lock原子操作;
- IA-32架构软件开发者手册对lock指令的解释:
- 会将当前处理器缓存行的数据立即写回到系统内存
- 这个写回内存的操作会引起 在其他cpu里缓存了该内存地址的数据无效(MESI协议)
7.查看关键汇编码
image解释如下:
- add ……:表示initFlag = true;这条语句地汇编语言实现,其中rsp为工作内存(寄存器)
- *putstatic……:表示该条语句对应的JVM指令码
- prepareData09……:表示代码在源文件中的信息
8.并发编程三大特性
可见性,原子性,有序性
volatile保证可见性和有序性,但不保证原子性(如下图,每个线程进行++时候,可能会丢失掉很多++操作),保证原子性需要借助synchronized这样的锁机制
image感谢你看到这里,我是程序员麦冬 ,一个java开发从业者,深耕行业六年了,每天都会分享java相关技术文章或行业资讯
欢迎大家关注和转发文章,后期还有福利赠送!
网友评论