多線程頻繁寫入同一緩存行上的不同 volatile 變數,導致 CPU 核心間緩存一致性協議(MESI)頻繁無效化/傳輸,劇烈降低吞吐。現象:邏輯無競爭,實際嚴重競爭。
@Contended(JEP 142)sun.misc.Contended 註解指示 JVM 在標註欄位/類前後插入 128 字節填充(2×典型緩存行 64B),將熱點變數隔離到獨立緩存行,消除偽共享。
-XX:-RestrictContended(預設限制非 JDK 內部使用)。contentionGroup 分組,同組欄位緊鄰、組間隔離。-XX:+PrintFieldLayout)// 類級
@Contended class ContendedClass {
long a, b, c, d;
}
// 輸出:實例欄位區起始 @140,結束 @288,中間 128B 填充
// 欄位級
class FieldContended {
long plain1, plain2, plain3, plain4;
@Contended long contended1;
}
// 輸出:plain 群 @12-@24,contended1 孤立 @156,前後填充至 @288
// 分組
class GroupContended {
@Contended("g1") long f1;
@Contended("g1") long f2; // 同組緊鄰
@Contended("g2") long f3; // 不同組隔離
long plain;
}
// 輸出:plain @12,g1 群 @148-@152,g2 @284,各組前後填充
class VolatileLong {
@Contended("g1") volatile long v1;
@Contended("g1") volatile long v2;
@Contended("g2") volatile long v3;
@Contended("g2") volatile long v4;
}
// 測試 1:同組寫入(t0 寫 v1/v2,t1 寫 v3/v4)→ 無偽共享
// 測試 2:跨組寫入(t0 寫 v1/v3,t1 寫 v2/v4)→ 有偽共享
// 測試 3:無 @Contended 基準
| 場景 | 耗時 (ns) | 相對基準 |
|---|---|---|
| 同組寫入(無偽共享) | 16.8B | 1.0x |
| 跨組寫入(偽共享) | 39.2B | 2.3x 慢 |
| 無 @Contended | 38.1B | 2.3x 慢 |
結論:正確分組下 @Contended 能將偽共享開銷降至基準線水平。
-XX:-RestrictContended 解禁,生產環境請評估相容性風險。sun.misc 非公開介面,未來版本可能變更/移除(Java 12+ 仍保留)。@jdk.internal.vm.annotation.Contended(同樣需解禁)。long p1, p2, p3, p4, p5, p6, p7; volatile long value;(7×8=56B 接近 64B)。LongAdder/LongAccumulator 內部分段計數,根治計數器偽共享。@State(Scope.Thread) 線程局部狀態避免共享。@Contended 是 JVM 級偽共享消除利器,配合 -XX:-RestrictContended 可在關鍵熱點路徑顯著提升多核擴展性。生產使用建議:核心基礎設施組件(框架、中間件)可引入,業務代碼優先考慮 LongAdder、線程局部、無鎖資料結構等標準 API 方案。