Java 并发 - 硬件层面分析

Java 并发理论基础-硬件层面分析

概述:
计算机中最核心的组件是CPU、内存、磁盘(I/O设备),CPU的计算速度是非常快的,内存次之、最后是IO设备比如磁盘。为了合理利用 CPU 的高性能,平衡这三者的速度差异,计算机体系结构、操作系统、编译程序都做出了贡献。

硬件层面分析
1、为了均衡与内存的速度差异,CPU 增加了缓存

目前主流CPU通常采用三层缓存

  • 一级缓存(L1):主要用于缓存指令(L1P)和缓存数据(L1D),指令和数据是分开存储的。
  • 二级缓存(L2):二级缓存的指令和数据是共享的,二级缓存的容量会直接影响CPU的性能,越大越好。
  • 三级缓存(L3 ):作用是进一步降低内存的延迟,同时提升海量数据计算的性能。三级缓存属于核心共享的,因此只有1个。

但是这样会引起cpu的缓存一致性

缓存不一致的问题,在多核CPU的系统中,比较容易出现。假设主存有一个x,值为5。cpu0和cpu1都从主存中加载x到自己的缓存中。此时cpu0更改x的值为8。此时cpu3的缓存中的x的值还是5,数据出现了不一致。

2、为了解决cpu的缓存一致性;cpu层面提供了总线锁和缓存锁

总线锁
处理器提供的一个LOCK信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占共享内存。这样的做法代价十分昂贵,于是为了降低锁粒度,CPU引入了缓存锁。
当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行时,会使用总线锁。

缓存锁
缓存锁的核心机制就是基于缓存一致性协议来实现的,即一个处理器的缓存回写到内存会导致其他处理器的缓存无效,MESI是一种比较常见的缓存一致性协议实现。

MESI协议

  • M(Modified):被修改的。该状态的数据,只在本CPU缓存中存在,其他CPU没有。同时,对于内存中的值来说,是已经被修改了,但还没更新到内存中去。也就是说缓存中的数据和内存中的数据不一致。
  • E(Exclusive):独占的。该状态的数据,只在本CPU缓存中存在,且并没有被修改,与内存数据一致。
  • S(Share):共享的。该状态的数据,在多个CPU缓存中同时存在,且与内存数据一致。
  • I(Invalid):无效的。本CPU中的这份缓存数据已经失效。

从cput读写数据角度:

CPU读数据:当CPU需要读取数据时,如果其缓存行的状态是I的,则需要从内存中读取,并把自己状态变成S,如果不是I,则可以直接读取缓存中的值,但在此之前,必须要等待其他CPU的监听结果,如其他CPU也有该数据的缓存且状态是M,则需要等待其把缓存更新到内存之后,再读取。

CPU写数据:当CPU需要写数据时,只有在其缓存行是M或者E的时候才能执行,否则需要发出特殊的RFO指令(Read Or Ownership,这是一种总线事务),通知其他CPU设置缓存无效(I),这种情况下性能开销是相对较大的。在写入完成后,修改其缓存状态为M。

但是这样会引起cpu在这期间处于阻塞状态。

3、为了解决cpu因为总线锁或缓存锁导致的阻塞问题,cpu引入了store buffer、invalidate queue

Store Bufferes
基于存储缓存Store Bufferes,CPU将要写入内存数据先写入Store Bufferes中,同时发送消息,然后就可以继续处理其他指令了。当收到所有其他CPU的失效确认(Invalidate Acknowledge)时,数据才会最终被提交。
Store Bufferes的引入提升了CPU的利用效率,但又带来了新的问题:缓存中的数据并不是最新的,所以CPU需要先读取Store Bufferes中是否有值。如果有则直接读取,如果没有再到自己缓存中读取,这就是所谓的”Store Forward“。

invalidate queue失效队列
CPU将数据写入Store Bufferes的同时还会发消息给其他CPU,由于Store Bufferes空间较小,且其他CPU可能正在处理其他事情,没办法及时回复,这个消息就会陷入等待。为了避免接收消息的CPU无法及时处理Invalid失效数据的消息,造成CPU指令等待,就在接收CPU中添加了一个异步消息队列。消息发送方将数据失效消息发送到这个队列中,接收CPU返回已接收,发送方CPU就可以继续执行后续操作了。而接收方CPU再慢慢处理”失效队列“中的消息。

但是这样会因为指令执行的顺序性,造成可见性问题

4、为了解决指令执行的顺序性,造成可见性问题,CPU层面提供了内存屏障

CPU层面提供了三类内存屏障:

  • 写屏障(Store Memory Barrier):告诉处理器在写屏障之前将所有存储在存储缓存(store
    bufferes)中的数据同步到主内存。也就是说当看到StoreBarrier指令,就必须把该指令之前所有写入指令执行完毕才能继续往下执行。
  • 读屏障(Load Memory Barrier):处理器在读屏障之后的读操作,都在读屏障之后执行。也就是说在Load屏障指令之后就能够保证后面的读取数据指令一定能够读取到最新的数据。
  • 全屏障(Full Memory Barrier):兼具写屏障和读屏障的功能。确保屏障前的内存读写操作的结果提交到内存之后,再执行屏障后的读写操作。

总之,CPU内存屏障的作用可以通过防止CPU乱序执行来保证共享数据在多线程下的可见性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值