并发中的伪共享问题

伪共享问题的表现是:并发的修改在一个缓存行中的多个独立变量,看起来是并发执行的,但实际在CPU处理的时候,是串行执行的,并发的性能大打折扣。

这个涉及到 MESI(缓存一致性协议),参考链接:伪共享问题

伪共享的原因就是 CPU 在 Invalid 的时候,是会直接废除一行的!

如果 两个变量 (a,b) 同时在一个 Cache Line 中,处理器A修改了变量a ,那么处理器B中,这个 CacheLine 失效了,这个时候如果处理器B修改了变量b的话,就必须先提交处理器A的缓存,然后处理器B再去主存中读取数据!这样就出现了问题,ab在两个处理器上被修改,本应该是一个并行的操作,但是由于缓存一致性,却成为了串行!这样会严重的影响并发的性能!

解决方案:

Java中提供给了我们两种方案:

填充法Contended 注解

  1. 填充法:就是 扩大对象的大小,这样,就可以一个缓冲行中,只存在一个对象!这样,就不会导致结果是串行执行了!
public class DataPadding{
    long a1,a2,a3,a4,a5,a6,a7,a8;//防止与前一个对象产生伪共享
    int value;
    long modifyTime;
    long b1,b2,b3,b4,b5,b6,b7,b8;//防止不相关变量伪共享;
    boolean flag;
    long c1,c2,c3,c4,c5,c6,c7,c8;//
    long createTime;
    char key;
    long d1,d2,d3,d4,d5,d6,d7,d8;//防止与下一个对象产生伪共享
}

上面的代码使用,填充法,对象的属性在内存行中的布局如下

value , modifyTime


flag


createTime
key

  1. Contended 注解法:Java1.8 中提供了Contended注解,使用这个注解,VM必须设置 -XX:-RestrictContended

如果在类型上添加Contended注解,那么这个类的对象的每个属性都会在不同的CacheLine

如果在属性上设置Contended,那么可以指定哪些 属性 处于一个CacheLine

@SuppressWarnings("restriction")
public class ContendedGroupData {
    @sun.misc.Contended("group1")
    int value;
    @sun.misc.Contended("group1")
    long modifyTime;
    @sun.misc.Contended("group2")
    boolean flag;
    @sun.misc.Contended("group3")
    long createTime;
    @sun.misc.Contended("group3")
    char key;
}

这个伪共享的注解,在 LongAddrConcurrentHashMap 中得到了很多的运用,对于并发的修改一个对象中的多个属性的时候,应该防止伪共享!

你可能感兴趣的:(并发中的伪共享问题)