【Java 并发】【一】CPU多级缓存模型以及Java内存模型
1 前言
在进行 Java 并发学习的时候,我们需要有个前置基础的概念和知识理清楚,然后才能进行后面深入的学习。这节我们就来看看CPU多级缓存模型以及Java内存模型。
2 CPU多级缓存模型
我们先从CPU的多级缓存架构讲起,下面画个图整体说下CPU多级缓存架构:
2.1 高速缓存的意义
- CPU与主存运行速度的差异:CPU是计算机的大脑,是负责执行指令的;自身的频率和指令执行的速度非常快,一秒执行的指令大概10^9级别的;内存的的速度要比CPU慢上好几个级别,每秒处理的速度大概是10^6的级别的。
- CPU长时间空闲:如果CPU要频繁的访问主存的话,每次都需要等待很长的时间,执行性能就会低,大部分时间都在等待主存返回数据,没有发挥出CPU的性能。
那么引入高速缓存,减少CPU等待时间,提升运行效率:
2.2 多核CPU的多级缓存架构
如上图所示,现代计算机一般都是多核CPU的,其中每个CPU都有自己的高速缓存,其中主内存是共用的。
读取数据的时候先从主内存读取到自己的高速缓存中,CPU需要数据时先从自己的高速缓存中查找,找不到再去主内存中拉取,同时刷入自己的高速缓存中。
通过引入了一层高速缓存,由于高速缓存中很多时候都已经缓存好数据了,CPU直接读取就可以了,不需要再去访问主存,同时高速缓存的速度接近于CPU,这样CPU就节省等待的时间啦。
那么这里我们是不是就会有这样的困惑,每个CPU都有自己的缓存吗?这样不会导致各个CPU的缓存之间数据不一致的问题吗?这个问题我们后续会讲。
3 JAVA内存模型(JMM)
为什么要设计JAVA内存模型?
CPU多级缓存模型,只是一个规范,但是底层基于这个规范的实现还是有很多种的。比如:
- 基于这个模型之上不同的计算机厂商可能对这个模型底层的实现不一样,包括底层运行的指令集、有的还基于多级缓存模型之上还引入了写换从器、无效队列等。
- 不同的操作系统windows、linux的指令集不一样。
JAVA语言为了应对上述的问题:不同的系统、不同的计算机厂商的底层实现不同。基于它们之上设计了一个JAVA内存模型(JMM)的规范,就是为了屏蔽底层操作系统、厂商之间的差异性。
JAVA程序运行时只需要关注JMM(JAVA内存模型),而不需关注底层系统、厂商指令的差异性,这些由JMM去适配不同的系统和厂商的指令集,这样就能实现一次编写,“到处乱跑”的跨平台效果。
其实就是一句话:为了屏蔽操作系统、不同的计算机厂商的底层的差异性,实现跨平台的无差异性运行的效果。
3.1 JAVA内存模型结构
那么我们来看一下Java内存模型的结构:
JAVA内存模型定义了一个规范啊。那就是每个线程都有一个工作内存,线程操作共享变量的时候需要从主内存读取到工作内存,然后再传递给工作线程使用。共享变量修改后先刷新到工作内存,然后再刷新回主内存。这个JAVA内存模型是基于我们上边的CPU多级缓存架构之上建立的。
3.2 JMM 八种原子操作
看了上面的图,JAVA内存模型的结构我基本知道了,但是JMM这怎么工作的呢,JMM定义了8种指令,它工作的时候就是通过这8种指令来操作内存的。包括怎么锁定变量、怎么将数据从主存传到工作内存、怎么传给正在被CPU调度的线程、修改之后CPU怎么传回工作内存、工作内存又怎么传递回主存:
-
lock(锁定):把主内存中的一个共享变量标记为一个线程独享的状态
-
unlock(解锁):把主内存的变量从线程独享的lock状态中解除出来
-
read(读取):把主内存的一个共享变量传输工作内存中
-
load(载入):把从主内存传输到工作内存的共享变量,赋值给工作内存中的变量副本
-
use(使用):把工作内存中的变量副本的值,传递给执行引擎CPU
-
assign(赋值):执行引擎(CPU)执行完之后,把修改过的变量值重新赋值给工作内存中的变量副本
-
store(存储):把工作内存中修改过共享变量的值传递到主内存中
-
write(写入):把传递到主内存中的变量值,重新写回给主内存的共享变量
我们画个图来理解下x++操作:
上面只有六种指令,还有另外两种lock、unlock指令是咋用的?我们再来看看:
4 小结
这节我们看了下CPU多级缓存以及JMM内存模型和8中基本指令操作的过程,其中我们能够感受到的就是当有了缓存,大大提高了执行的吞吐量,那么继而带来的一致性如何解决,并发如何去管控成为了关键,后续我们会来分开讲哈,这节就到这里了,有理解不对的地方欢迎指正哈。