public class LongAdder extends Striped64 implements Serializable {
private static final long serialVersionUID = 7249069246863182397L;
public LongAdder() {
}
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
// ...
}
@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);
}
// ...
}
ConcurrentHashMap中用Contended注解自动对CounterCell来进行填充:每个CounterCell记录了对应Node的键值对数目。这样每次计算size时累加各个CounterCell就可以了
ConcurrentHashMap中CounterCell以数组形式保存,而数组在内存中是连续存储的,CounterCell中只有一个long类型的value属性,这样CPU会缓存CounterCell临近的CounterCell,于是就形成了伪共享。
总结
- CPU缓存是以缓存行为单位进行操作的。产生伪共享问题的根源在于不同的核同时操作同一个缓存行。
- 可以通过填充来解决伪共享问题,Java8 中引入了
@sun.misc.Contended
注解来自动填充。 - 并不是所有的场景都需要解决伪共享问题,因为CPU缓存是有限的,填充会牺牲掉一部分缓存。