并发编程——JMM(Java内存模型)

一、现代计算机理论模型与工作原理

冯诺依曼计算机模型
在这里插入图片描述

  • 控制器(Control)
  • 运算器(Datapath)
  • 存储器(Memory)
  • 输入(Input system)
  • 输出(Output system)

现代计算机主要结构基础
在这里插入图片描述
CPU数据读取速度: 寄存器 > L1 > L2 > L3 > 内存

    CPU缓存是为了弥补内存速度跟不上cpu处理数据速度而诞生的,当今的cpu处理数据速度非常之快,相比之下内存的速度要慢得多,如果cpu直接从内存那里取得数据就会导致cpu大部分时间处于等待状态,浪费资源,于是高速缓存cache就出现了。

  • 寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果;

  • 一级缓存也是内置在CPU内部并且是与CPU同速运行,可以有效的提高CPU的运行效;

  • 二级缓存一般都集成在cpu中,但有分为芯片内部和外部两种,集成在芯片内部的二级缓存与CPU同频率二级缓存(即全速二级缓存),而集成在芯片外部的二级缓存的运行频率是CPU的运行频率的一半(即半速二级缓存),因此运行效率较低;

  • 三级缓存集成在CPU外部;

  • 内存通过总线和CPU交互,所以速度最慢;
        在拥有三级缓存的CPU中,其实只有大约5%的数据需要从内存中调用,那么这样也将会是这大大提高了CPU的效率。
        读取数据时,CPU依次去寄存器、一级缓存中、二级缓存、三级缓存和内存中去找。从内存依次读取到三级缓存、二级缓存、一级缓存和寄存器中。在数据计算完成后,计算结果被写到内存中的时机是不确定的,如果此时其他的CPU也对数据进行操作,将导致缓存一致性问题。为了让CPU在计算完数据后,立即将计算结果同步到内存中去,出现了Mesi缓存一致性协议

CPU内部结构
在这里插入图片描述
CPU多核(多CPU)缓存架构
为了解决缓存一致性问题,有两种解决办法:

1. 总线加锁
    在CPU复制内存中数据到缓存中去使用时,为了避免其他CPU对数据进行操作,会对数据进行加锁,其他CPU对于加锁的数据是不能进行读写操作的。
    显而易见,主线加锁导致性能问题,所以目前大部分情况下都不会再进行主线加锁的操作
2. Mesi缓存一致性协议
Mesi代表缓存行1的四种状态,
在这里插入图片描述
在这里插入图片描述
缓存一致性是通过一个总线来实现的

  • 当数据被加载到CPU1缓存中时,如果没有其他CPU复制数据到缓存行中,则CPU1缓存中的缓存行处于独享(E)状态,同时缓存行(总线嗅探机制)监听总线消息;
  • CPU2加载数据到缓存中,CPU1和CPU2中的缓存行状态变成共享(S)状态。
  • CPU1对缓存行数据进行修改时,缓存行状态变成修改(M)状态;
  • CPU1向内存中写数据时,CPU2中的缓存行监听到 bus 的消息时,CPU2中的缓存行变成无效(I)状态;bus 回写数据到主内存中;CPU1中缓存行变成独享(E)状态
  • CPU2如果还想要访问数据时,由于缓存行状态为(I),则需要从主内存中再次加载数据,CPU1和CPU2的缓存行状态再次都变成共享(S)状态
        当CPU同时修改缓存行状态为独享时,在一个指令周期中,会进行总线裁决,只会有一个指令有效,其余的缓存行置为无效(I)状态。
        根据 FIFO最久未使用法,缓存会被一层一层清理出去。

缓存行失效问题

  1. 如果数据长度大于一个缓存行,缓存行锁会失效,缓存一致性协议失效,会加总线锁
  2. CPU本身不支持缓存一致性协议

线程

进程是系统分配资源的基本单位;
线程是调度CPU的基本单元,CPU分配时间片,执行线程中的程序片段。每一个线程都有一个程序计数器、一组寄存器、栈

线程分类
在这里插入图片描述

  1. 用户级线程:ULT 用户进程自己创建的线程
    用户进程中创建多个线程,进程执行时CPU,实际上时进程内部的多个线程在执行,当其中一个线程发生阻塞时,进程内的所有线程阻塞。
    好处:避免过度创建线程,避免大量上下文切换;
    问题:线程表的管理、调度等需要进程自己实现;
  2. 内核级线程:KLT 只有内核空间才能创建
    内核空间中会有一个线程表,所有的线程依附于内核空间,每一个线程可以被视为一个轻量级的进程。

Java1.2之前使用ULT,Java1.2版本之后使用KLT

用户空间划分

  1. 内核空间
  2. 用户空间:用户空间通过内核空间提供的接口,创建内核级线程

CPU特权级别

  • Ring0:只有内核空间才有权限调用
  • Ring1
  • Ring2
  • Ring3:用户空间

Java线程与内核线程的关系
    1:1 映射关系
在这里插入图片描述
jvm调用操作系统的库调度器,创建对应的内核线程。

Java线程生命状态

在这里插入图片描述
为什么用到并发

  1. 充分利用多核CPU的计算能力
  2. 方便进行业务拆分。提升应用性能

并发产生的问题

  1. 高并发场景下,导致频繁的上下文切换;
        线程切换时,需要保存线程A的指令、程序指针、中间数据到内核空间中的Tss任务状态段;再去指向性线程B
  2. 临界去线程安全问题,容易出现死锁,产生死锁就会造成应用不可用;
    windows使用jstack查看进程状态。
  3. 其他问题…

JMM模型

在这里插入图片描述
JMM围绕原子性、有序性和可见性展开。
JMM是一组规范,屏蔽底层具体实现由不同的JVM去实现。

Java内存模型内存交互操作

  1. lock(锁定):作用于主内存的变量,把一个变量标记为一条线程独占状态
  2. unlock(解锁):作用于主内存的变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
  3. read(读取):作用于主内存的变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
  4. load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中
  5. use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎
  6. assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋给工作内存的变量
  7. store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作
  8. write(写入):作用于工作内存的变量,它把store操作从工作内存中的一个变量的值传送到主内存的变量中

    Java内存模型只要求上述8大操作(原子操作)必须按顺序执行,而没有保证必须是连续执行

Java内存模型内存同步规则

  1. 不允许一个线程无原因地(没有发生过任何assign操作)把数据从工作内存同步回主内存中
  2. 一个新的变量只能在主内存中诞生,不允许在工作内存中直接使用一个未被初始化(load或者assign)的变量。即就是对一个变量实施use和store操作之前,必须先自行assign和load操作。
  3. 一个变量在同一时刻只允许一条线程对其进行lock操作,但lock操作可以被同一线程重复执行多次,多次执行lock后,只有执行相同次数的unlock操作,变量才会被解锁。lock和unlock必须成对出现。
  4. 如果对一个变量执行lock操作,将会清空工作内存中此变量的值,在执行引擎使用这个变量之前需要重新执行load或assign操作初始化变量的值。
  5. 如果一个变量事先没有被lock操作锁定,则不允许对它执行unlock操作;也不允许去unlock一个被其他线程锁定的变量。
  6. 对一个变量执行unlock操作之前,必须先把此变量同步到主内存中(执行store和write操作)

  1. 缓存中的最小存储单元,32 | 64 | 128bit ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值