缓存一致性以及JMM模型理解

计算机多CPU模型:

缓存一致性以及JMM模型理解

 1.多CPU:
        一个现代计算机通常由两个或者多个CPU,如果要运行多个程序(进程)的话,假如只有
一个CPU的话,就意味着要经常进行进程上下文切换,因为单CPU即便是多核的,也只是多个
处理器核心,其他设备都是共用的,所以 多个进程就必然要经常进行进程上下文切换,这个代
价是很高的。

2.cpu多核:

        一个现代CPU除了处理器核心之外还包括寄存器、L1L2L3缓存这些存储设备、浮点运算
单元、整数运算单元等一些辅助运算设备以及内部总线等。一个多核的CPU也就是一个CPU上
有多个处理器核心,这样有什么好处呢?比如说现在我们要在一台计算机上跑一个多线程的程
序,因为是一个进程里的线程,所以需要一些共享一些存储变量,如果这台计算机都是单核单
线程CPU的话,就意味着这个程序的不同线程需要经常在CPU之间的外部总线上通信,同时还
要处理不同CPU之间不同缓存导致数据不一致的问题,所以在这种场景下多核单CPU的架构就
能发挥很大的优势,通信都在内部总线,共用同一个缓存。

3.cpu寄存器:

        每个CPU都包含一系列的寄存器,它们是CPU内内存的基础。CPU在寄存器上执行操作的速度远大于在主存上执行的速度。这是因为CPU访问寄存器的速度远大于主存。

4.cpu缓存:

        即高速缓冲存储器,是位于CPU与主内存间的一种容量较小但速度很高的存储器。由于CPU的速度远高于主内存,CPU直接从内存中存取数据要等待一定时间周期,Cache中保存着CPU刚用过或循环使用的一部分数据,当CPU再次使用该部分数据时可从Cache中直接调用,减少CPU的等待时间,提高了系统的效率。一级Cache(L1 Cache)二级Cache(L2 Cache)三级Cache(L3 Cache)

5.cpu内存:

        所有cpu都可以访问主内存。主内存通常要比cpu的缓存打的多。cpu读取存储器xx的值,只需要一步:直接读取。cpu读取L1的值需要1-3步:把cache行锁住,把某个数据拿来,解锁,如果没锁住就慢了。cpu要取L2的某个值,先到L1中取,L1中不存在的时候,L2开始加锁,把L2的数据复制到L1,在执行读L1的过程,上面的3步,在解锁。L3的一样的,先从L1->L2->L3。cpu最复杂:通知内存控制器占用总线带宽,通知内存加锁,发起内存读请求,等待回应,回应数据保存到L3(如果没有就到L2),再从L3/2到L1,再从L1到CPU,之后解除总线锁定。

多线程环境下存在的问题

   缓存一致性问题

        在多处理器系统中,每个处理器都有自己的高速缓存,而它们又共享同一主内存 (MainMemory)。基于高速缓存的存储交互很好地解决了处理器与内存的速度矛盾,但是 也引入了新的问题:缓存一致性(CacheCoherence)。当多个处理器的运算任务都涉及同一 块主内存区域时,将可能导致各自的缓存数据不一致的情况,如果真的发生这种情况,那同步 回到主内存时以谁的缓存数据为准呢?为了解决一致性的问题,需要各个处理器访问缓存时都 遵循一些协议,在读写时要根据协议来进行操作,这类协议有MSI、 MESI(IllinoisProtocol)、MOSI、Synapse、Firefly及DragonProtocol,等等。主要讲解MESI协议:加了缓存一致性的CPU模型

缓存一致性以及JMM模型理解

 

      MESI缓存一致性协议缓存一致性以及JMM模型理解

  假设主内存中存在x变量,当cpu1 读取到变量x(E)的时候,其就会在总线上产生一个嗅探去监听主内存的中x,当cpu2读取到x变量的时候,cpu的嗅探就会将cpu1的缓存中的x(E) ->x(S),cpu2缓存中读取到x(S)同时也会产生一个嗅探去监听x的状态。当cpu1修改其值后,其x(S)->x(M)并会通知总线准备刷新内存,此时cpu2中的嗅探就会将cpu2中的x(S)->x(I)。

问:如果两个cpu同时将x(S)->x(M),即同时修改。一个指令周期内此时就会通过cpu裁决,谁裁决胜利了即采用谁的。被遗弃的cpu的修改要重新修改,取决于指令码的实现。比如java中unsafe类。

问:何时缓存一致性会失效?

       1.当数据横跨2个缓存行的时候,即失效,采取效率更为低下总线锁的形式。

        2.cpu不支持缓存一致性协议。

        ps:缓存行时缓存的最小存储单位。有可能是32字节,64字节,128字节,根据具体的操作系统而定

JMM模型:

        JMM与JVM内存区域的划分是不同的概念层次,更恰当说JMM描述的是一组规则,通过 这组规则控制程序中各个变量在共享数据区域和私有数据区域的访问方式,JMM是围绕原子 性,有序性、可见性展开。JMM与Java内存区域唯一相似点,都存在共享数据区域和私有数 据区域,在JMM中主内存属于共享数据区域,从某个程度上讲应该包括了堆和方法区,而工作 内存数据线程私有数据区域,从某个程度上讲则应该包括程序计数器、虚拟机栈以及本地方法 栈。

缓存一致性以及JMM模型理解

 Java内存模型与硬件内存架构的关系

        通过对前面的硬件内存架构、Java内存模型以及Java多线程的实现原理的了解,我们应该 已经意识到,多线程的执行最终都会映射到硬件处理器上进行执行,但Java内存模型和硬件内 存架构并不完全一致。对于硬件内存来说只有寄存器、缓存内存、主内存的概念,并没有工作 内存(线程私有数据区域)和主内存(堆内存)之分,也就是说Java内存模型对内存的划分对硬件内 存并没有任何影响,因为JMM只是一种抽象的概念,是一组规则,并不实际存在,不管是工作 内存的数据还是主内存的数据,对于计算机硬件来说都会存储在计算机主内存中,当然也有可 能存储到CPU缓存或者寄存器中,因此总体上来说,Java内存模型和计算机硬件内存架构是一 个相互交叉的关系,是一种抽象概念划分与真实物理硬件的交叉。(注意对于Java内存区域划分 也是同样的道理)

缓存一致性以及JMM模型理解

 线程中切换的时候的,上下文切换,会将该线程占用的cpu中的寄存器以及缓存中的一些指令码,以及中间数据保存到主内存(Liunx Tss任务状态栏中)中去。

JMM存在的必要性

        明白了Java内存区域划分、硬件内存架构、Java多线程的实现原理与Java内存模型的具 体关系后,接着来谈谈Java内存模型存在的必要性。由于JVM运行程序的实体是线程,而每个 线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数 据,线程与主内存中的变量操作必须通过工作内存间接完成,主要过程是将变量从主内存拷贝 的每个线程各自的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,如 果存在两个线程同时对一个主内存中的实例对象的变量进行操作就有可能诱发线程安全问题。如图所示:

        

 缓存一致性以及JMM模型理解

 以上关于主内存与工作内存之间的具体交互协议,即一个变量如何从主内存拷贝到工作内 存、如何从工作内存同步到主内存之间的实现细节,Java内存模型定义了以下八种操作来完 成。

JMM-同步八种操作介绍

        (1)lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态

        (2)unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的 变量才可以被其他线程锁定

        (3)read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中, 以便随后的load动作使用

        (4)load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作 内存的变量副本中

        (5)use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎

        (6)assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存 的变量

        (7)store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中, 以便随后的write的操作

        (8)write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送 到主内存的变量中

        如果要把一个变量从主内存中复制到工作内存中,就需要按顺序地执行read和load操作, 如果把变量从工作内存中同步到主内存中,就需要按顺序地执行store和write操作。但Java内 存模型只要求上述操作必须按顺序执行,而没有保证必须是连续执行。

上一篇:多线程并发之JMM&volatile详解


下一篇:Java并发理论总结,这似乎是目前最实用的HashTable知识总结