Java并发——缓存一致性

版权声明:本文为博主原创文章,转载注明出处即可。 https://blog.csdn.net/bskfnvjtlyzmv867/article/details/84404963

I. CPU多级缓存

CPU的时钟频率非常的快,跑起来的速度远远超过了内存、硬盘。《码农翻身》形象的比喻CPU为阿甘,跑的速度是内存的100倍,硬盘的1000多万倍。如果直接靠CPU直接和内存打交道,那么CPU要等待太久,浪费资源。

我们平时编写的程序中,包含着很多连续创建的数组、对象,各种循环、递归、调用同一函数等,其实本质上符合了局部性原理局部性原理具体是指在CPU访问存储设备时,无论是存取数据还是存取指令,都趋于聚集在一片连续的区域中,主要包含时间与空间上的局部性。

  • 时间局部性 (Temporal Locality):如果一个数据被访问,那么在近期它很可能还会被再次访问。
  • 空间局部性 (Spatial Locality):如果一个数据被访问,那么与它相邻的数据也很快会被访问。

有了这样的特性,就可以在CPU和内存之间添加高速缓存存储CPU经常访问的数据,如下图所示,缓解CPU和内存速度严重差异的问题。

高速缓存

CPU和cache之间进行数据/指令存取,而内存(主存)和高速缓存则通过系统总线来实现通信。程序员编写的程序以及数据被加载到内存中后,随后被加载到高速缓存,CPU执行完指令处理完数据将结果写回高速缓存中,最后高速缓存再写回内存。

然而,由于CPU的运算速度不断提升,很快超越了一级缓存的数据 I\O 能力,CPU厂商们又开始引入了多级的缓存结构。多核的CPU至少拥有着不止一个一级缓存,当高速缓存中拷贝了内存中同一个数据多个副本时,CPU操作的是每一个副本,而如何保证副本与副本之间,以及副本与主存的数据一致呢?

多级缓存

主要利用的是缓存一致性的原则。

II. 缓存一致性

MESI协议中的缓存状态

状态 含义 监听任务
M 被修改
Modified
因为缓存行刚被修改,数据应是独一无二的,与主存中的数据不一致,与其他CPU的缓存中对应数据也不相同。但是一定会在将来某个时间点写回主存中,这个时间点一定是在其他CPU读取自己的(主存相应的)缓存之前。 被修改的缓存行必须时刻监听所有想读该缓存行对应的主存/其他CPU缓存的操作,因为要确保在CPU读取操作之前把被修改的缓存行写回主存并将状态变为 S
E 独享的
Exclusive
被修改状态的缓存行要将数据写回主存,此时可以认为是独享的状态。只有自己的缓存和主存中数据一致,其他CPU对应的缓存行还没有更新。但是一定会在将来其他CPU读取对应的缓存行之前变为共享状态。 独享的缓存行也必须监听其他CPU缓存读取主存中对应缓存行的操作,一旦有了这种操作,该缓存行需要变成 S 状态。
S 共享的
Shared
该状态意味该缓存行可能被多个CPU缓存,并且各个缓存中的数据与主存中的数据是一致的。当有一个CPU修改了该缓存行中的数据,该缓存行变为被修改状态,其他CPU中对应的缓存作废,变为无效状态 I 对于该缓存行来说,要监听其他缓存使该缓存行无效。
I 无效的
Invalid
缓存行作废,无效。 \

缓存行状态变化

本地缓存当前状态 事件 行为 本地缓存状态变化 触发缓存行状态变化 其他缓存行状态变化
M 本地缓存读取本地缓存数据 从本地缓存读取数据,状态不变。触发缓存就是本身。其他缓存则因为本地缓存是M,早就是I了。 M->M M->M I
本地缓存写入本地缓存数据 本地缓存行状态为M,继续修改数据,状态还是M。触发缓存就是本身。其他缓存则因为本地缓存是M,早就是I了。 M->M M->M I
触发缓存读取本地缓存对应数据 触发缓存行和其他缓存行状态都为I,触发缓存行想要读取对应数据,必然得从主存走。本地缓存行中数据将回写到主存,本地状态变为E。等到触发缓存从主存中获取到数据,所有缓存行都同步,状态变为S M->E->S I->S I->S
触发缓存写入本地缓存对应数据 触发缓存行想要先写对应缓存,发现本地缓存数据还没同步,所有先走触发缓存读取本地缓存对应数据的流程。由于触发缓存要写入数据,所以本地缓存和其他缓存会变为I,此时触发缓存行状态相当于E,等触发缓存写入成功最后会变为M,。 M->E->S->I I->S->E->M I->S->I
E 本地缓存读取本地缓存数据 从本地缓存读取数据,状态不变。触发缓存就是本身。其他缓存则因为本地缓存是E,早就是I了。 E->E E->E I
本地缓存写入本地缓存数据 本地缓存行已经回写到主存,又继续修改,状态又变为M,其他缓存行继续I。触发缓存就是本身。 E->M E->M I
触发缓存读取本地缓存对应数据 本地缓存行已经回写到主存,触发缓存读取数据之前需要先进行本地缓存行数据的同步,所有缓存行数据变为S。同步完成,触发缓存行也就正好读完。 E->S I->S I->S
触发缓存写入本地缓存对应数据 触发缓存行想要写对应数据,先经历触发缓存读取本地缓存对应数据的过程。当共享完成,触发缓存行对数据进行更新,其他缓存行都变为I,随后自身变为E再变为M。 E->S->I I->S->E->M I->S->I
S 本地缓存读取本地缓存数据 共享状态读取不影响。 S S S
本地缓存写入本地缓存数据 共享状态下,本地缓存进行修改,其他缓存行将先变成I,此时相当于本地缓存行为E,修改后再变成M。触发缓存就是本地缓存。 S->E->M S->E->M S->I
触发缓存读取本地缓存对应数据 共享状态读取不影响。 S S S
触发缓存写入本地缓存对应数据 共享状态下,触发缓存想要修改,则本地缓存和其他缓存先变为I,触发缓存则变成E。修改完成后,触发缓存继续变成M S->I S->E->M S->I
I 本地缓存读取本地缓存数据 1. 如果其他缓存没有对应数据,状态为I,本地缓存从主存读取数据,缓存行变成E
2. 如果其他缓存有对应数据,且状态为M,则先将数据更新到主存,本地缓存再从主存中读取,两个缓存对应的缓存行都变为S
3. 如果其他缓存有对应数据,且状态为S/E,意味着主存已经更新,可以直接读取,这时两个缓存对应的缓存行都变为S
I->E
I->S
I->S
I->E
I->S
I->S
I->I
M->E->S
E->S/S->S
本地缓存写入本地缓存数据 1. 如果其他缓存没有对应数据,一开始所有缓存行都为无效,所以需要先从主存中取数据同步,写入所有缓存,缓存行状态变为E。随后进行本地缓存更新,状态变为M
2. 如果其他缓存有数据但是缓存行状态为E,本地缓存行为无效,所以需要先从主存中取数据同步,写入本地缓存,缓存行状态变为E。随后进行本地缓存更新,状态变为M
3. 如果其他缓存中有对应数据,且状态为M,则先将其他缓存中的数据更新到主存,本地缓存再从主存中读取,所有缓存行都为S。随后进行本地缓存更新,本地缓存行状态变为M,其他缓存变为I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->E->I
I->S->I
E->S->I
M->E->S->I
触发缓存读取本地缓存对应数据 既然是本地缓存行是I,触发缓存行的操作与它无关。 I \ \
触发缓存写入本地缓存对应数据 既然是本地缓存行是I,触发缓存行的操作与它无关。 I \ \

参考文章

猜你喜欢

转载自blog.csdn.net/bskfnvjtlyzmv867/article/details/84404963