伪共享问题(false sharing)
对于解释伪共享问题,就需要了解一下缓存行的相关概念。缓存行是主存复制到高速缓存的最小单位,一般情况下缓存行的大小为32~128字节(通常为64字节)。
在多线程程序执行的过程中,有可能将2个或多个需要频繁修改的变量存储在同一个缓存行当中。这样以来,会频繁的造成缓存头失效的问题。如下图所示:
从编码的角度,为了解决上面的问题,可以使用额外字段来填充缓存行数据。从而达到不同变量之间占用不用的缓存行,增加缓存的命中率。优化后如下图所示:
简单事例
下面看一个例子,看一看填充缓存行前后的运行效果对比。
笔者在自己的PC上面测试,加填充代码和不加填充代码的前后效果如下:
从结果看来,有填充代码的比无填充代码的效率提高了3倍多。下面是笔者测试的,线程数与耗时的测试结果:
Java8中的支持
Java8会对CPU 伪共享问题做一些优化,这里面使用到了 @sun.misc.Contended 。它可以使用在class 和 field 上面,官方也不推荐使用。我们在写业务代码时,使用了没有什么效果。其内容如下:
ConcurrentHashMap
在Java8中的ConcurrentHashMap 中我们可以看到如下:
上面CounterCell 是用于分段锁的大小统计,由此可以看出JDK 底层对@Contended 做了CPU Cache 优化。
Thread