JMM思维导图

JMM是java程序员必备的技能之一,虽说没必要将知识图谱中各个知识点,像背书一样把他流利的背下来,最起码,要理解消化。

几乎每种语言都有自己的内存模型。JMM,java memory model,java内存模型总有自己的来龙去脉。

这个要先从硬件 -  内存模型 - java内存模型讲起。

硬件基础

计算机执行程序,每条指令代码都是在CPU中执行的,指令代码是用来操作数据的,而数据最开始都是存储在内存中。(这里埋下一个点,以后专门写一篇文章,来写元数据,代码是用来操作数据的,但是代码又何尝不是数据呢)。

刚开始,铢两相称,但是随着CPU和内存技术的发展,CPU远远的领先了。所以从内存读写的速度远远小于CPU的执行速度,导致CPU每次操作内存都需要大量的等待。所以,解决方式就是高速缓存(这个思路,就好比是系统配置放在一张数据库的表中,每次获取配置都从数据库拿,就慢了,可以将这个配置读取之后都放到缓存中),高速缓存保存一份数据的拷贝,高速缓存的特点就是速度快,内存小,并且昂贵,现在技术上还做不到将高速缓存的技术直接应用到内存。

随着CPU能力的不断提升,一层缓存就慢慢不能解决需求了,所以就衍生了多级缓存。

现阶段,按照数据的读取顺序,各个厂商的CPU缓存可以分为一级缓存L1,二级缓存L2,三级缓存L3,L1和L2 cpu独享,L3共享,个别多核CPU,L1独享,L2和L3共享。技术和成本是依次递减,容量是递增的。

缓存之间是要遵循一个MESI的协议规定的,并且不同的CPU厂商在这个协议基础上,有自己的改进,就比如说intel i7的缓存,可以通过cache line直接将数据送给其他cpu。这个MESI可以暂时放放,不影响我们接下来要说的事情。

总之,当CPU要读取数据时,先从一级缓存找,没有,就从二级缓存找,如果还是没有就从三级缓存找。(这里又会有一个引申的问题,缓存中到底应该存哪些数据呢,可以参考LRU等等算法)

缓存跟内存通信中, 使用的就是缓存行,cache line,缓存行是缓存和内存交换数据的最小单位,一般为64字节。即每次缓存和内存之间进行数据交换的时候,都是以字节对齐的连续64字节的内存块整个进行(java volatile 关键字也是使用了字节对齐的技术)。

JMM思维导图

现在我们抽象一下,减少下复杂度。假设现在有一台多核cpu的机器,每个core都是单线程操作,主存中有一个变量X,这个X是一个热数据,所以core1 和core2 的L1的中都会有一份这个x的拷贝副本,如果core1改变了L1中的x的内容,这个时候core2 的L1中的x值就可以说是失效的,那么这个时候就会出现缓存一致性的问题。

当前core1中,处理的代码逻辑本来应该是将一个新的变量赋值给x,然后将x的新值赋给y,处理器可能会对代码进行乱序执行处理,如果这个顺序颠倒了,那么就会出现y的值本来应该存储更改过的x值,但是实际上却保存的是更改之前的x值,这个就是处理器优化

内存模型

并发编程主要的问题,主要就分为三种,原子性,有序性,可见性。

原子性:就是指一个完整的操作在CPU要不就执行完,要不就不执行

有序性:就是指代码按照代码的顺序执行完

可见性:就是多个线程访问同一个变量时,一个线程修改了变量的值,能够立马对另一个线程可见 。

为了并发编程中可以满足以上三个特点,就有一个抽象,推出了内存模型的概念。

而上面,硬件相关的部分中,多核cpu的告诉缓存提出的缓存一致性问题,实际上就抽象出来内存模型的可见性。cpu的乱序执行抽象出来就是内存模型中的原子性和有序性,后面讲述java虚拟机的时候,也会提到java虚拟机的JIT即时编译器也会做指令重排的,参与抽象出内存模型中的原子性和有序性。

总结起来,内存模型是什么,他定义了共享内存系统多线程操作的行为规范,他是一种抽象,抽象了硬件中缓存一致性,处理器优化等问题。

内存模型解决并发问题主要采用两种方式:限制处理器优化和使用内存屏障

JMM Java内存模型

现在就好解释了把,java内存模型,是一种符合内存模型规范,java自己实现的内存模型,因为java是跨平台的,java内存模型屏蔽了各种硬件和操作系统的差异,保证了java程序在各种硬件和平台对内存访问都一致的规范。

Java内存模型,现阶段的内存模型都是建立在jdk5新内存模型基础上的,主要要参考JSR-133 官方定义的规范。

java内存模型规定了所有的变量都存储在主内存中(堆中),每条线程都有自己的工作内存(线程栈),线程的工作内存(栈)中保存了主存中(堆)变量的拷贝。线程都是在自己的工作内存中(线程栈中)操作主存中(堆)变量的副本拷贝,不能直接操作主存(堆)中的变量。

JMM的作用就是决定了如何以及何时在工作内存(线程栈)和主存(堆)中数据同步。

所以再来总结下,JMM就是一种规范,规范了多线程中如何通过共享内存进行通信,解决本地内存数据不一致、编译器会对代码指令重排序、cpu会对代码乱序执行等问题。

这里再提一嘴,线程间如何通信,主要有两种方式,一种就是上面说到的通过共享内存,另外一种就是信号量。这个还是可以后面专门写一篇文章来讲述。

Java内存模型的实现

java中提供了一系列和并发处理相关的关键字,比如volatile、synchronized、final、JUC包等等。之前我也写过了几个JUC java.until.concurrent包下的类的示例,比如说reentrantlock、countdownlatch等等相关封装好的包,实际上JUC包的关键类就是aqs, abstractsynchronizedquee,通过子类继承这个队列同步器,实现模版方法,并且将子类组合到自定义同步器中使用相关的方法即可,这个之前有文章写过了。

其实以上这些就是java内存模型封装了底层的实现后提供给程序员试用的一些关键词。 所以下面这个图就是整个的学习路线,后面找一些关键的讲述下。

JMM思维导图