@sun.misc.Contended 介绍
@sun.misc.Contended 是 Java 8 新增的一个注解,对某字段加上该注解则表示该字段会单独占用一个缓存行(Cache Line)。
这里的缓存行是指 CPU 缓存(L1、L2、L3)的存储单元,常见的缓存行大小为 64 字节。
(注:JVM 添加 -XX:-RestrictContended 参数后 @sun.misc.Contended 注解才有效)
@sun.misc.Contended 的作用——避免伪共享
那么单独使用一个缓存行有什么作用?
为了性能和节省空间,一个缓存行可能不止一个变量,里面还会存储着其他变量,即伪共享。但是这对于共享变量,会造成性能问题:
CPU 修改某共享变量 A 时会先锁定 A 所在的缓存行,并且把其他 CPU 缓存上相关的缓存行设置为无效。如果被锁定或失效的缓存行里,还存储了其他不相干的变量 B,则会造成不必要的开销,因为其他线程此时会访问不了 B,或者由于缓存行失效需要重新从内存中读取加载到缓存。而如果 A 单独使用一个缓存行就可以避免此问题。
Java 8 之前的方案
在 Java 8 之前,是通过代码里手动添加属性的方式解决的,如:
class LongWithPadding { long value; long p0, p1, p2, p3, p4, p5, p6; }
一个 long 占 8 个字节,再添加 7 个 long 属性就会变成 64 个字节,刚好是一个缓存行大小。
但是注意,Java 7 开始 JVM 做优化时可能会把不用的变量给去掉,所以这种方法并不推荐使用。
适用场景
主要适用于频繁写的共享数据上。如果不是频繁写的数据,那么 CPU 缓存行被锁的几率就不多,所以没必要使用了,否则不仅占空间还会浪费 CPU 访问操作数据的时间。