伪共享问题的表现是:并发的修改操作,在CPU的底层实际上是串行的在执行!
这个涉及到 MESI(缓存一致性协议),参考链接:伪共享问题
伪共享的原因就是 CPU 在 Invalid
的时候,是会直接废除一行的!
如果 两个变量 (a,b)
同时在一个 Cache Line
中,处理器A
修改了变量a
,那么处理器B中,这个 CacheLine
失效了,这个时候如果处理器B
修改了变量b
的话,就必须先提交处理器A
的缓存,然后处理器B
再去主存中读取数据!这样就出现了问题,a
和b
在两个处理器上被修改,本应该是一个并行的操作,但是由于缓存一致性,却成为了串行!这样会严重的影响并发的性能!
解决方案:
Java中提供给了我们两种方案:
填充法
和 Contended
注解
- 填充法:就是
扩大对象的大小
,这样,就可以一个缓冲行中,只存在一个对象!这样,就不会导致结果是串行执行了!
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
- 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;
}
这个伪共享的注解,在 LongAddr
和 ConcurrentHashMap
中得到了很多的运用,对于并发的修改一个对象中的多个属性的时候,应该防止伪共享!
网友评论