美文网首页
java内存模型

java内存模型

作者: 蜗牛写java | 来源:发表于2021-08-03 00:29 被阅读0次

    1.内存模型(JMM)

    1.1什么是Java内存模型?

    Java内存模型将内存分为主内存和工作内存两大部分;主内存用来存储线程之间共享数据,工作内存则是每个线程独享内存,存储对应线程使用到的共享数据副本

    java内存模型JMM.png

    Java内存模型和硬件中的多核CPU、CPU对应的缓存(1级缓存、2级缓存、3级缓存)、以及内存之间的设计异曲同工;CPU的每个核可以跑不通的线程,由于CPU执行指令速度远远大于内存读写速度,为了平衡两者差距,高速缓存就引入了计算机体系中,高速缓存位于CPU和内存之间;读写速度能力 CPU>L1>L2>L3>Memory(越靠近CPU侧,处理能力越高,读写越快);存储数据能力Memory>L3>L2>L1>CPU(越靠近Memory侧,存储能力越强);计算机执行程序时,CPU执行程序时,会频繁的使用数据从内存中拷贝到缓存中,这样CPU运算时可以更快的读取到运算的数据;当CPU运算结束,又将数据从高速缓存写回到内存中。

    1.2.内存模型中工作内存和主内存的区别?
    • 主内存:所有线程共享的区域;存储共享数据的区域
    • 工作内存:线程私有的存储区域;主要存储线程使用的主内存中数据的副本
    1.3.内存模型带来什么问题

    工作内存和主内存中数据不一致问题

    1. 原子性

      线程1 和 线程2 并发执行 +1操作,并同步到主内存中

    内存模型-原子性.png
    1. 可见性

      内存模型-可见性.png
    1. 重排序
    public class Test{
      private int a = 0;
      private int b = 0;
      
      public void setAAndB() {
        a = 1;
        b = 2;
      }
      
      public void changeB() {
        if (a == 1) {
          b = 4;
        }
        System.out.println("b=" + b);
      }
    }
    
    内存模型重排序.png
    1.4.volatile关键字有哪些语义

    volatile主要面对JMM带来的问题(工作内存和主内存中数据不一致)而产生的一种解决方案

    语义有

    1. volatile修饰的变量具有可见性
    2. volatile修饰的变量读写原子性(如32位系统上的Long变量)
    3. volatile修饰的变量,读写操作不会参与前后指令的重排序

    具体理解

    1. volatile修饰的变量具有可见性

      对于一个volatile变量的读,总能看到(任意线程)对这个volatile变量最后的写入值

      写:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存中

      读:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效,重新从主内存中读取共享变量到工作内存中

      volatile可见性.png
    1. volatile修饰的变量读写原子性(单步操作)

      对于任意单个volatile变量的读、写具有原子性;但是对于i++这种复合操作不具有原子性

    2. volatile修饰的变量,读写操作不会参与前后指令的重排序

      是否可以重排序 第二个操作 第二个操作 第二个操作
      第一个操作 普通读/写 volatile读 volatile写
      普通读/写 不可以重排序
      volatile读 不可以重排序 不可以重排序 不可以重排序
      volatile写 不可以重排序 不可以重排序

      总结:

      • 第一个操作为volatile读时,不管第二个操作是什么,都不可以重排序
      • 第二个操作为volatile写时,不管第一个操作是什么,都不可以重排序
      • 第一个操作为volatile写,第二个操作为volatile读时,不可以重排序
    1.5.volatile是怎么实现的

    还是围绕三个语义来看

    1. 可见性

      在写入volatile变量的时候,JVM会向CPU发送Lock指令,Lock指令具有两个作用

      • 将当前工作内存中数据写入到主内存中
      • 涉及其他线程对应的CPU核中缓存的数据设置无效(CPU消息一致性协议和嗅探功能),之后读会重新重主内存拉取最新数据
    2. 原子性(读写原子性)

      线程是CPU调度的基本单位。CPU有时间片的概念,会根据不同的调度算法进行线程调度。当一个线程获得时间片之后开始执行,在时间片耗尽之后,就会失去CPU使用权。所以在多线程场景下,由于时间片在线程间轮换,就会发生原子性问题。

      为了保证原子性,需要通过字节码指令monitorenter和monitorexit,但是volatile和这两个指令之间是没有任何关系的。

      所以,volatile是不能保证(复合操作)原子性的

    3. 重排序(有序性)

      主要靠内存屏障(硬件层面的指令,它可以禁止屏障前后的指令进行重排序)

      • 在每个volatile写操作的前面插入一个StoreStore屏障
      • 在每个volatile写操作的后面插入一个StoreLoad屏障
      • 在每个volatile读操作的后面插入一个LoadLoad屏障
      • 在每个volatile读操作的后面插入一个LoadStore屏障
      volatile-重排序机制.png

    相关文章

      网友评论

          本文标题:java内存模型

          本文链接:https://www.haomeiwen.com/subject/sdssvltx.html