CPU缓存是由缓存行组成的,通常是 64字节(常用处理器的缓存行是64字节的,比较旧的处理器缓存行是32字节)。
当前有2个比较简单的对象A和对象B,分别有一个long属性。那这2个对象的实际占用内存大小为:16(对象头)+8(long属性)=24字节。那么这2个对象可能被同时存储在一个缓存行中。若当前有2个线程分别处理A对象和B对象,这样会导致这2个线程对应的cpu的寄存器失效,需要每次去内存中重新读取数据,就出现伪共享问题。那么这个时候,就可以采用补齐(padding)的办法,在这个类型中加上public long a,b,c,d,e;这五个无用的属性定义,使得这个类型的一个实例占用内存达到64字节,这样这个类型的伪共享问题就得到了解决。
java测试代码
public final class FalseSharing implements Runnable {
public static int NUM_THREADS = 4; // change
public final static long ITERATIONS = 500L * 1000L * 1000L;
private final int arrayIndex;
private static VolatileLong[] longs;
public FalseSharing(final int arrayIndex) {
this.arrayIndex = arrayIndex;
}
public static void main(final String[] args) throws Exception {
// Thread.sleep(10000);
System.out.println("starting....");
if (args.length == 1) {
NUM_THREADS = Integer.parseInt(args[0]);
}
longs = new VolatileLong[NUM_THREADS];
for (int i = 0; i < longs.length; i++) {
longs[i] = new VolatileLong();
}
final long start = System.nanoTime();
runTest();
System.out.println("duration = " + (System.nanoTime() - start));
}
private static void runTest() throws InterruptedException {
Thread[] threads = new Thread[NUM_THREADS];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new FalseSharing(i));
}
for (Thread t : threads) {
t.start();
}
for (Thread t : threads) {
t.join();
}
}
public void run() {
long i = ITERATIONS + 1;
while (0 != --i) {
longs[arrayIndex].value = i;
}
}
// 这段代码用来告知编译器不要去除p1、p2等字段,1.7之后jvm会自动去除无用的字段
public static long sumPaddingToPreventOptimisation(final int index)
{
VolatileLong v = longs[index];
return v.p1 + v.p2 + v.p3 + v.p4 + v.p5 + v.p6
+ v.p7 + v.p8 + v.p9 + v.p10 + v.p11 + v.p12 + v.p13 + v.p14;
}
public final static class VolatileLong {
public long p1, p2, p3, p4, p5, p6, p7; // 注释
public volatile long value = 0L;
public long p8, p9, p10, p11, p12, p13, p14; // 注释
}
}