java内存模型(jmm)概念初探

1.和java内存结构的区别:

    很多人会把jmm和Java内存结构搞混,网上搜到的一些文章也是如此,java内存结构就是我们常说的堆,栈,方法区,程序计数器..., 当jvm虚拟机启动的时候,会初始化这些内存区域。而java内存模型是一套虚拟机规范,它遵循虚拟机规范定义了一套用于处理多线程下对数据的原子性,可见性,有序性的规则,它在每个jvm下的实现都可以是不同的,只要符合虚拟机规范就可以。

 

    要知道java内存模型设计的意义,首先我们可以了解一下操作系统的内存模型:

2.操作系统内存模型

        在最早的时候,cpu和内存直接打交道,那个时候两者速度是差不多的,但是后来随着cpu发展迅速,内存速度越来越跟不上了,cpu每次获取数据的时候就干瞪眼,于是乎就出现了高速缓存,寄存器这些玩意,但是这玩意是每个核心独有的,你其他核心访问的时候还是从你自己的寄存器-几级高速缓存-主存获取,主存是共享的,但是你总不能每次改一下,就刷一次主存,那也太慢了,没啥意义,所以你肯定得攒一段时间再写到主存,于是乎,一个共享的变量,你改了,但是还没到你要刷到主存的时候,我也改了,但是我读不到你缓存里的数据,那肯定就会出现缓存不一致的问题。    

        除了这种情况,cpu为了让内部的运算单元能被充分利用,可能会打乱代码顺序执行,我们听过的指令重排就是java虚拟机层面类似的优化,那么这种问题如何能解决呢其实这些就是我们所说的原子性,可见性,顺序性发生的原因,那么操作系统层面是怎么保证的呢,我总不能为了速度,不管他对不对了吧。为了解决数据一致性的问题,通常各个处理器访问缓存都遵循一些协议,主要有:MSI、MESI(Illinois Protocol)、MOSI、Synapse、Firefly 及 Dragon Protocol 等,下面我们主要介绍一下MESI:

 

3.MESI

  在多核处理器中,缓存都是独立的,但是多核之间需要共享数据,那么我们如何保证这些数据的一致性?

  在MESI中,每个缓存行都有4中状态:M ,E, S,I

  M: M全称Modified,它标识这行数据在本缓存被修改了,和内存中不一致,但本缓存是有效的,如果别的CPU内核要访问主存中这块数据,则该缓存行数据必须先写回主存,状态改为S

  E: 全称Exclusive,表示当前数据有效,但数据只有该缓存和主存有,当别的Cpu读时,状态改为S,如果被修改了,改为M

  S: 全称Shared,表示当前缓存行在其他缓存中也有,且自身未修改,缓存行可以被抛弃(缓存行有自己的淘汰策略)

  I: 全称invalid,表示当前缓存行是无效的。

基于上面的知识,其实可以大致知道MESI就是通过在读写数据时,通知所有拥有这块数据的核心修改自身的状态,达到在任意时刻任意核心读到的数据都是一致的,而且不需要实时更新数据到主存或其他缓存。

java内存模型和操作系统的是比较类似的,它的所有操作基本上都是对底层的映射,它定义了线程和主存之间的抽象的映射关系:

线程间共享的变量存在主存中,线程的独有的缓存存储变量的副本,其实大体上和操作系统理解起来是一个道理。

 

 

 

 

  jmm是比较抽象的一个概念,必须要结合操作系统的内存模型来结合理解,像操作系统为了提升性能,加了多级缓存,加了寄存器,并且会优化要执行的代码。而加了之后也是有代价的,那就是会造成所谓的并发三大特性:

    1.可见性:就是当一个共享变量被其他线程修改后,当前线程可以立刻读到改变后的值。

为什么会出现可见性问题?

    如上图所示,每个线程都有自己的本地缓存(为了效率),而本地缓存就会导致如线程2将一个共享变量变更了,刷到主存中,但线程1不知道我这个值变了,还是会去读缓存,就会导致线程2的修改对线程1不可见。

如何解决可见性问题:

    1.volatile关键字

    2.通过直接调用内存屏障storeFence()

    3.synchronized关键字

    4.Lock保证

    5.final关键字

  2.有序性:

    用一句通俗但又不易懂的话来说就是眼见不一定为实,怎么理解呢,jvm为了效率,有时候会对指令进行重排,它不一定会按照你写的代码一步步执行,当然有前后依赖关系的肯定不会重排。

如何解决有序性问题:

    1.volatile关键字

    2.通过直接调用内存屏障storeFence()

    3.synchronized关键字

    4.Lock保证  

  3.原子性:

    要么都执行,要么不执行,不管你有多少个线程同时执行,我这个原子性的操作一定是不会被打断或者拆分执行,相当于是这个操作以及不能再被拆分了。

如何保证原子性:    

    1.CAS的方式

    2.synchronized关键字

    3.Lock保证

 

本章节主要介绍了操作系统内存模型的设计,延伸到jmm的设计,以及这样的设计带来的影响,操作系统会通过实现一些一致性协议来解决这样的问题,java则通过上述一些方式来处理三大特性的问题,后续章节将介绍这些方式解决三大特性的原理。

posted @ 2022-03-16 13:04  吃肉不长肉的小灏哥  阅读(304)  评论(0编辑  收藏  举报