@Contended 通过在 Cell.value 前后插入填充字节,确保其独占一个缓存行,从内存布局层面切断伪共享;需 JVM 启用 -XX:+UnlockExperimentalVMOptions -XX:-RestrictContended 且使用 JDK 内置注解才生效。

LongAdder 的 Cell 类用 @Contended 注解,核心目的就是从内存布局层面切断伪共享(False Sharing)——它不靠逻辑隔离,而是让每个 Cell.value 独占一个缓存行。
为什么 @Contended 能起作用?
CPU 缓存以缓存行为单位加载数据(通常 64 字节),如果两个线程更新的变量落在同一缓存行里,哪怕彼此无关,也会因 MESI 协议反复使对方缓存失效。@Contended 的作用,就是在被标记字段的前后插入大量填充字节(padding),确保该字段在内存中“独占一整行”。
-
Cell是个静态内部类,其value字段被@Contended标记 - JVM 在对象布局时,会在
value前后各填充约 64 字节(具体取决于-XX:ContendedPaddingWidth设置) - 结果:即使多个
Cell实例在堆内存中连续分配,每个value也绝不会和另一个value共享缓存行
@Contended 生效的前提条件
这个注解不是“加了就自动生效”,必须满足几个硬性条件:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- JVM 启动时需显式开启:
-XX:+UnlockExperimentalVMOptions -XX:-RestrictContended - 必须使用 JDK 自带的注解:
jdk.internal.vm.annotation.Contended(JDK 9+),不能自己定义同名注解 - 只对实例字段有效,
static字段无效;且不能用于 bootstrap classloader 加载的类(如java.*包下的类) - 建议配合
-XX:ContendedPaddingWidth=64显式指定填充宽度,匹配主流 CPU 缓存行大小
它解决的是哪一层问题?
伪共享常发生在高频更新的独立状态之间,比如 AtomicLong[] 数组中相邻元素、RingBuffer 的序列号字段等。@Contended 不改变程序逻辑,也不做任何同步,它只是让 JVM 在内存里把关键字段“隔开”——就像给每位程序员配一张专属办公桌,而不是合用一张长桌。
立即学习“Java免费学习笔记(深入)”;
- 它不缓解线程竞争逻辑,而是消除竞争带来的硬件级副作用
- 效果直接体现在缓存未命中率下降、CAS 失败率降低、吞吐量提升
- 代价是对象内存占用上升(一个
@Contended字段通常多占 128~256 字节)
没开 @Contended 怎么办?
如果生产环境无法启用该参数(比如老版本 JDK 或受限容器),可以用手动填充替代:
- 在字段前后声明多个
long或long[]字段作为 padding - 例如:
private long p1, p2, p3, p4, p5, p6, p7; private volatile long value; private long q1, q2, q3, q4, q5, q6, q7; - 这种方式兼容性更好,但维护成本略高,且需注意字段顺序与 JVM 字段重排策略

















