CPU 緩存以 緩存行 為單位讀寫內存(典型 64 字節)。當多線程修改**不同但相鄰**的變量,這些變量恰好落在**同一緩存行**時,會導致緩存行在核心間頻繁來回傳輸、失效、同步,嚴重拖垮性能——這就是偽共享。

圖解:核心 1 寫變量 X,核心 2 寫變量 Y,X/Y 同一緩存行 → 緩存行在兩核心間「乒乓」傳遞,經過 L3 甚至跨插槽,性能暴跌。
利用對象頭 (8/12 字節) + 6 個 long (48 字節) = 56/60 字節,接近 64 字節緩存行大小,使每個對象獨佔一行。
public final class VolatileLong {
public volatile long value = 0L;
public long p1, p2, p3, p4, p5, p6; // 填充字段
}
JDK 7 引入逃逸分析、標量替換,**判定填充字段無用而優化掉**,導致對象縮水,偽共享復發。
public class VolatileLongPadding {
public volatile long p1, p2, p3, p4, p5, p6;
}
public class VolatileLong extends VolatileLongPadding {
public volatile long value = 0L;
}
@Contended 註解(終極方案)JDK 8 (JEP 142) 引入 sun.misc.Contended,由 JVM 自動在對象/字段周圍插入填充,完全透明、無侵入、跨平台穩定。
import sun.misc.Contended;
@Contended
public class VolatileLong {
public volatile long value = 0L;
}
-XX:-RestrictContended(默認限制僅 JDK 內部類使用)。public class VolatileLong {
@Contended public volatile long value1 = 0L;
@Contended public volatile long value2 = 0L;
@Contended public volatile long value3 = 0L;
@Contended public volatile long value4 = 0L;
}
public class VolatileLong {
@Contended("group0") public volatile long value1 = 0L;
@Contended("group0") public volatile long value2 = 0L; // 與 value1 緊鄰
@Contended("group1") public volatile long value3 = 0L;
@Contended("group1") public volatile long value4 = 0L; // 與 value3 緊鄰
// group0 與 group1 之間、與普通字段之間有填充隔離
}
// 測試 1:線程 0 寫 value1/value2 (同組);線程 1 寫 value3/value4 (同組) → 無偽共享
// 測試 2:線程 0 寫 value1/value3 (跨組);線程 1 寫 value2/value4 (跨組) → 有偽共享
// 測試 3:無 @Contended 基準
結果(納秒,越小越快):
| 場景 | 耗時 | 說明 |
|---|---|---|
| 同組無衝突 (測試 1) | 16.8s | 最快,緩存行獨享 |
| 跨組有衝突 (測試 2) | 39.2s | 慢 2.3 倍,偽共享顯現 |
| 無註解基準 (測試 3) | 38.1s | 與衝突組接近,驗證註解有效 |
import sun.misc.Contended;(屬於 JDK 內部 API,編譯需 -XDignore.symbol.file 或配置 Maven Compiler Plugin 允許內部 API)。-XX:-RestrictContended,否則註解無效(許多教程漏提此點)。@jdk.internal.vm.annotation.Contended(模塊化後新位置),同樣需 --add-exports java.base/jdk.internal.vm.annotation=ALL-UNNAMED。@Contended 為正解,JVM 級支援,配合分組機制完美平衡性能與內存。-XX:-RestrictContended,否則等於白寫。偽共享是無聲的性能殺手,掌握 @Contended 讓你在高併發寫入場景中游刃有餘。