引言

在对称多处理器(SMP)系统中,每个处理器均有一个本地高速缓存。内存系统必须保证高速缓存的一致性。
当不同处理器上的线程修改驻留在同一高速缓存行中的变量时就会发生假共享,结果导致高速缓存行无效,并强制执行更新,进而影响系统性能。
如图:
false-sharing.gif

线程0和线程1会用到不同变量,它们在内存中彼此相邻,并驻留在同一高速缓存行。高速缓存行被加载到CPU0和CPU1的高速缓存中(灰色箭头)。
尽管这些线程修改的是不同变量(红色和蓝色箭头),高速缓存行仍会无效,并强制内存更新以维持高速缓存的一致性。

缓存系统中是以缓存行(cacheline)为单位存储的。缓存行是2的整数幂个连续字节,一般为32-256个字节。最常见的缓存行大小是64个字节。一个Java的long类型是8字节,因此在一个缓存行中可以存8个long类型的变量。所以,如果你访问一个long数组,当数组中的一个值被加载到缓存中,它会额外加载另外7个,这会带来一些优势。但是也有伪共享问题,比如两个线程,修改long数组的第一个与第七个,会频发发生缓存失效,影响性能。解决办法就是填充,在JDK8中提供了@sun.misc.Contended注解来避免伪共享,
如Striped64类中的Cell:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
* Padded variant of AtomicLong supporting only raw accesses plus CAS.
*
* JVM intrinsics note: It would be possible to use a release-only
* form of CAS here, if it were provided.
*/
@sun.misc.Contended static final class Cell {
volatile long value;
Cell(long x) { value = x; }
final boolean cas(long cmp, long val) {
return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
}

// Unsafe mechanics
private static final sun.misc.Unsafe UNSAFE;
private static final long valueOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> ak = Cell.class;
valueOffset = UNSAFE.objectFieldOffset
(ak.getDeclaredField("value"));
} catch (Exception e) {
throw new Error(e);
}
}
}

还有ConcurrentHashMap中的CounterCell:

1
2
3
4
5
6
7
8
/**
* A padded cell for distributing counts. Adapted from LongAdder
* and Striped64. See their internal docs for explanation.
*/
@sun.misc.Contended static final class CounterCell {
volatile long value;
CounterCell(long x) { value = x; }
}

伪共享(FalseSharing)
避免并发现线程之间的假共享
伪共享(FalseSharing)和缓存行(CacheLine)大杂烩
伪共享(falsesharing),并发编程无声的性能杀手
Java8使用@sun.misc.Contended避免伪共享