先来了解现代计算机硬件体系
-
CPU和内存是计算机中两个比较核心的东西,他们之间会频繁的进行交互,随着cpu发展越来越快,内存的读写的速度已经远远跟不上CPU的处理速度,所以CPU厂商在CPU上加了一个 高速缓存,用于cpu计算时的数据存储,这样就不用频繁的去和内存进行读写降低效率。我们在看CPU硬件参数的时候,也会看到有这样的参数:
image.png -
一般高速缓存有3级:L1,L2,L3,CPU与内存的交互,就发生了变化,CPU不再与内存直接交互,CPU会先去L1中寻找数据,没有的话,再去L2中寻找,然后是L3,最后才去内存寻找(更准确的来说,应该是CPU中的寄存器去寻找)。
image.png -
看起来一切都很美好,但是随着科技的进步,CPU厂商们搞事了,推出了多核CPU提高计算机处理性能,每个CPU上又有高速缓存,CPU与内存的交互就变成了下面这个样子:
image.png -
某个CPU需要修改某个数据,先去Cache中找,如果Cache中没有找到,去内存中找,然后把数据copy到自己的Cache中进行修改,然后再把修改完的数据刷新到主内存。
所以,多个cpu同时操作内存中的同一个数据,就会出现大家读取的数据不一致的情况,因为谁也不知道自己操作的这份数据是不是最新的,是否已经被另外一个cpu做过了修改,所以,引发了一个问题:缓存不一致。
解决这个问题的方法有很多,比如:
- 总线加锁(此方法性能较低,现在已经不会再使用)
- MESI协议
这是Intel提出的,MESI协议也是相当复杂,在这里我就简单的说下:当一个CPU修改了Cache中的数据,会通知其他缓存了这个数据的CPU,其他CPU会把Cache中这份数据的Cache Line置为无效,要读取数据的话,直接去内存中获取,不会再从Cache中获取了。(java中的volatile关键字)
当然还有其他的解决方案,MESI协议是其中比较出名的。
Java线程与硬件处理器
我们在Java中开启一个线程,最终Java也会交给CPU去执行。
具体的流程是:我们在使用Java线程,内部会调用操作系统(OS)的内核线程(Kernel-Level Thread),这种线程是操作系统内核(Kernel)直接支持的,内核通过调度器,对线程进行调度,并将线程交给各个CPU内核去处理。
如下图所示:java线程 — 操作系统线程 — CPU
JAVA内存模型
Java内存模型是一个抽象的概念,其实并不存在,它描述的是一种规范,这个规范可以这样解释:每个线程都有自己的工作内存,工作内存中保存的是主存中某些变量的拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。线程之间无法直接访问对方的工作内存中的变量,线程间变量的传递均需要通过主内存来完成。
image.png
Java内存模型中不管是本地内存,还是主内存的数据,最终都会存储在CPU(更准确的来说 是寄存器)、Cache、内存上。所以,java中的线程对应到CPU,主内存(也叫堆内存或共享内存)对应到计算机的内存,本地内存对应到高速缓存,中间通过操作系统进行调度
一句话:Java内存模型就是为了对应计算机cpu,缓存,内存,从而解决多线程对共享数据的读写一致性问题。
网友评论