1.说一下你对Java内存模型(JMM)的理解?
其实java内存模型是一种抽象的模型,具体来看可以分为工作内存和主内存。
JMM规定所有的变量都会存储再主内存当中,再操作的时候需要从主内存中复制一份到本地内存(cpu内存)再线程内部做计算,然后再回写到主内存
本地内存它其实也是JMM的 一个抽象概念,并不真实存在。具体来看是包括缓存、写缓冲区、寄存器以及其他的硬件和编译器优化。
从上图我们可以看到实际的内存模型包括了控制器和操作运算器,
同时工作内存就对应这里的 Ll 缓存或者 L2 缓存或者 CPU 寄存器。
2.说说你对原子性、可见性、有序性的理解?
我们围绕着java内存模型其实可以发现有很多问题,它是如何来保证原子性,可见性和有序性的呢?
想要知道这些 我们首先要知道什么是原子性,可见性和有序性
原子性:原子性说白了就是指一个操作是不可分割,不可中断的,一个线程在执行时,其他线程不会影响到它
那么**如何来保证原子性呢?**在java中为了保证原子性,提供了两个高级指令 monitorenter和moitorexit ,而具体实现来看如synchronized就可以保证原子性
可见性:首先为啥会有可见性问题?上面也说了是分为了工作内存和主内存,操作的时候,每个线程都是操作自己工作内存的数据,然后再会写到主内存,如果没有及时同步到主内存或者并发时,就会存在不一致的问题,总结来看可见性就是 某个线程修改了某一个共享变量的值时,其它线程能够立即知道这个修改
如何保证可见性呢? 在java实现中可以用volatile或者synchronized 以及final来保证可见性,具体实现原理下面再说
有序性:首先还是要明白为啥会存在有序性问题?其实这和指令重排有关,也就是说再java中存在指令重排–就是java文件中的内容会被编译,再执行前需要转化为cpu可以识别的指令,cpu再执行这些指令时,为了提升执行效率,在不影响最终结果的前提下,会对指令进行重排
因此就出现了有序性的问题,所以有序性 就是指 对于一个线程的执行代码,从前往后依次执行
如何来保证有序性呢? 具体来看还是可以用volatile 以及synchronized
volatile关键字可以禁止指令重排,而synchronized通过加锁的方式保证同一时刻只有一个线程在操作执行。
3 什么是指令重排?
说白了就是在程序执行时,为了提升效率,编译器 和执行器在执行指令时会对指令进行重排序,但是注意不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:
在单线程环境下不能改变程序运行的结果;
存在数据依赖关系的不允许重排序
这两个条件其实也就是两个规则as-if-serial和 happens-before;
需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。
所以总结来看 as-if-serial语义和happens-before这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度。
4 volatile实现原理了解吗?
首先我们要知道从java内存模型的角度来看 volatile有两个作用,保证可见性和有序性。
那么volatile是如何保证可见性的呢?
volatile保证可见性主要是通过lock前缀指令和MESI缓存一致性协议;
啥意思呢?也就是说如果一个变量被volatile修饰,那么当你对这个变量进行写操作时,jvm都会向处理器发送一条lock前缀指令,那么工作内存中的值就会被强制刷入到主内存中,而其他处理器的缓存由于遵守了缓存一致性协议,也就会把这个变量的值从主内存加载到自己的工作内存中,这样一来也就保证了可见性
volatile是如何保证有序性的呢?
volatile保证有序性主要是通过禁止指令重排,再具体来看就是禁止编译器和处理器的重排序
那么问题来了,它是怎么禁止指令重排的呢?
其实他的实现原理主要是基于内存屏障,即在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
而内存屏障主要包括 storestore,storeload,loadload,loadstore屏障
说白了这些内存屏障保证了代码程序会按照代码的先后顺序执行,进而来保证有序性。
未完待续啊,… 多多支持,加油💪🏻