Java8引入了@Contented这个新的注解来减少伪共享的发生。CPU在读取数据的时候并非一次读取一个字节,而是通过缓存行来进行读取。不同的CPU缓存行的长度并非一样,一般为64个字节长度。 比如有个数据类型内容如下:
public class MyNode {
volatile MyNode head;
volatile MyNode tail;
}
假设现在有两个线程A和B,线程A处理head头结点,线程B处理tail尾节点。理论上A线程和B线程修改不同的数据,之间应该不会有额外的影响。然而由于CPU加载数据的时候最小的额粒度为缓存行,比如当线程A来修改head会将锁住缓存行,将缓存的数据失效,这样在A线程修改的时候,那B线程只能等着A线程处理完,来获取最新的缓存行数据,这就发生了伪共享。伪共享也是并发编程里面隐藏的性能杀手。在1.8之前多数通过填充数据来解决。
public class MyNode {
volatile MyNode head;
volatile int a;
volatile int b;
.....
volatile MyNode tail;
}
这样通过数据的填充,讲head和tail放在了不同的缓存行,这样就不会相互影响。在1.8就可以通过注解@Contended来解决这个问题。
import sun.misc.Contended;
public class MyNode {
volatile MyNode head;
@Contended
volatile MyNode tail;
}