伪共享-并发

伪共享定义:

缓存系统中是以缓存行(cache line)为单位存储的,当多线程修改互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享。

可见性:

如果所有的操作都顺序执行,那就不会出现并发的问题,但是现在是多核CPU。为了解决并发造成的可见性问题,我们经常使用锁或者volatile关键字。使用锁来锁住代码块或者方法,就标识此处就是顺序执行。但是volatile来修饰共享变量,解决的是内存可见性问题时,一个缓存行修改完以后会把其他缓存置为无效,保证内存的可见性。

伪共享产生原因

为了解决CPU和内存速度不匹配的问题,我们正常会有三级缓存的存在,第一级和第二级缓存都是单CPU共享,3级缓存和内存是卡槽CPU共享。如果一个核的线程,要使用另一个核的前两级缓存行的数据(3级缓存行的数据已经可以共享),就需要另一个核主动把缓存行的数据同步过去。根据缓存的局部性原理(时间局部性和空间局部性),高速缓存在加载数据的时候,会把其附近的数据一起加载到高速缓存,以提高命中率。

正常我们共享基本类型,或者共享单个属性不会出现问题。但是当我们在使用数组对象时,对象包含共享变量X,该对象加载到C1的缓存行,根据局部性原理,会把其相邻的对象也同样加载进来,这样C1的缓存行就包含了该数组对应的多个对象。

(缓存行64字节,一个volitile修饰的Integer类型占用四个字节,不算对象头开销,一个缓存行可以加载8个int类型,也就是一个数组的连续区域)

如果C2此时要操作数组,就需要把C1的缓存行置为无效;C1使用数组,又会把C2的缓存行置为无效。如此反复,只能都从3级缓存加载数据。

解决:

缓存行填充,把一个对象空余出来的字段补满;

缓存行 64 = 4 + 对象头大小 + padding

原文地址:https://www.cnblogs.com/at20191018/p/12194091.html