1. 概述
什么是缓存行伪共享?
这个就需要说下计算机的硬件组成,CPU的高速缓存是由缓存行组成的,通常是64字节,并且它有效地引用主内存中的一块地址。在程序运行过程中,每次更新都从主内存中加载连续的64个字节。
假设a和b两个变量是相邻的,一个CPU核心线程会对a进行修改,另外一个CPU核心线程会读取b的值。当a变量被修改后其他包含a的缓存行都将失效,因为其他缓存行中的a不是最新值了。而后者读取b时,发现缓存行已经失效,需要从主内存中重新加载。
这样就出现了一个问题,b 和 a 完全不相干,每次却要因为 a 的更新需要从主内存重新读取,它被缓存未命中给拖慢了。如图所示
2. 如何避免伪共享
- 在两个 long 类型的变量之间再加 7 个 long 类型
class Pointer {
volatile long x;
long p1, p2, p3, p4, p5, p6, p7;
volatile long y;
}
- 重新创建自己的 long 类型,而不是 java 自带的 long
class Pointer {
MyLong x = new MyLong();
MyLong y = new MyLong();
}
class MyLong {
volatile long value;
long p1, p2, p3, p4, p5, p6, p7;
}
- 使用 @sun.misc.Contended 注解(java8)
@sun.misc.Contended
class MyLong {
volatile long value;
}
@sun.misc.Contended的原理是在使用此注解的对象或字段的前后各增加128字节大小的padding,从而让CPU将对象预读至缓存时占用不同的缓存行,这样,就不会造成对方缓存行的失效。
注:默认使用这个注解是无效的,需要在JVM启动参数加上 -XX:-RestrictContended才会生效,建议使用第三种方式。