为了解决计算机系统中的内存与CPU之间运行速度差的问题,会在CPU和主内存之间添加一级或者多级高速缓冲存储器(Cache)。这个Cache一把是被集成到CPU内部的,所以也叫CPU Cache。
当多个线程同时修改一个缓存行里面的多个变量时,由于只能有一个线程操作缓存行,所以相比于每个变量放到一个缓存行,性能会有所下降,这就是伪共享。
为何会出现伪共享 伪共享的原因是由于多个变量被放入一个缓存行中,并且多个线程同时去写入缓存行中不同的变量。因为缓存与内存交换数据的单位就是缓存行(利用程序运行的局部性原理加速程序运行)。
如何让避免伪共享 在JDK 8之前都是通过字节填充的方式填充缓存行,避免多个变量占用一个缓存行。
public final static class FilledLong{
public volatile long value = 0L;
public Long p1,p2,p3,p4,p5,p6;
}
JDK 8提供了一个sun.misc.Contended注解,用来解决伪共享问题。
@sun.misc.Contended
public final static class FilledLong{
public volatile long value = 0L;
}
这里用来修饰类,也可以修饰变量,在Thread类中。
在默认情况下,@Contended注解只用于Java核心类,比如rt包下的类。如果用户类路径下的类需要使用这个注解,则需要添加JVM参数:-XX:RestrictContended。填充的宽度默认为128,要自定义宽度可以设置为-XX:ContendedPaddingWidth参数。
小结 讲述了伪共享是如何产生的,以及如何避免,并证明多线程访问同一个缓存的多个变量时才会出现,单线程访问一个缓存行的多个变量反而会加速(双层for中,访问array【i】【j】比访问array【j】【i】要快 )。为高级知识LongAdder的实现原理奠定基础。
(注:java并发编程之美)



