Java-cacheline

291 阅读2分钟

缓存

我们都知道cpu的速度和内存不匹配,为了解决这个问题有了缓存。在不同厂商cpu中缓存的大小也不一样。以intel为例,缓存行为64k,也就是8个long

当有这么个场景的时候

volatile long arr = new long[2];
第一个线程对arr[0]进行操作
第二个线程对arr[1]进行操作

这两个线程当位于不同的cpu core的时候,缓存就会频繁的失效,更新,失效,更新
因为缓存一致性协议 intel是MESI,具体怎么保证的,太复杂了。我先放弃,可以立个flag,//TODO 学习MESI

记录一个MESI的文章一致性协议

伪共享

两个变量在同一个缓存行中,但是这是两个不相关的变量。有两个线程分别对齐进行读写,这样就导致缓存频繁的失效,导致效率低下。

先从本质上想想这个伪共享存在的问题如何改善。就是让不相关的变量不要出现在一个缓存行里,那么办法其实就来了。

伪共享解决方案

jdk1.6

这个代码旨在将value成员单独占据一个缓存行。为什么只有p1, p2, p3, p4, p5, p6呢?因为对象头也有体积。

    public final static class VolatileLong 
    { 
        public volatile long value = 0L; 
        public long p1, p2, p3, p4, p5, p6;
    } 

jdk1.7

上面1.6的写法在1.7中会失效,因为JAVA 7会优化掉无用的字段,这时候不是没有办法了,想不出来解决方案就上网查查资料

public class VolatileLongPadding {
    public volatile long p1, p2, p3, p4, p5, p6; // 注释  
}

public class VolatileLong extends VolatileLongPadding {
    public volatile long value = 0L;  
}

合理的利用继承解决了这个问题。

jdk1.8

在JAVA 8中,缓存行填充终于被JAVA原生支持了。JAVA 8中添加了一个@Contended的注解,添加这个的注解,将会在自动进行缓存行填充。

执行时,必须加上虚拟机参数-XX:-RestrictContended,@Contended注释才会生效。很多文章把这个漏掉了,那样的话实际上就没有起作用。

End

其实这也是多线程编程需要考虑的问题。学到老活到老...