傳統 synchronized/ReentrantLock 屬於悲觀鎖:假設衝突必發生,阻塞線程等待。高併發下上下文切換、內核態切換開銷大。無鎖採用樂觀鎖策略:假設衝突少見,嘗試直接更新,失敗則重試(自旋),避免線程掛起,吞吐量顯著提升。
CAS 由 CPU 指令 cmpxchg (x86) / LDXR/STXR (ARM) 保證原子性,不可分割。JVM 通過 Unsafe.compareAndSwapInt/Long/Object 內部方法暴露給 Java 層。
// 伪代码
boolean compareAndSet(V expected, V newValue) {
if (currentValue == expected) {
currentValue = newValue;
return true;
}
return false;
}
只有當內存值 等於預期值 時才更新為新值,返回成功;否則不修改,返回失敗。調用者通常在循環中重試 (自旋)。
| 類別 | 代表類 | 適用場景 |
|---|---|---|
| 基本類型 | AtomicInteger, AtomicLong, AtomicBoolean | 計數器、序列號、開關標誌 |
| 引用類型 | AtomicReference<V> | 對象引用原子更新、無鎖堆棧/隊列節點 |
| 數組類型 | AtomicIntegerArray, AtomicReferenceArray | 數組元素獨立原子更新 |
| 字段更新器 | AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater | 對現有類的 volatile 字段賦予原子性,不改動原類結構 |
| 帶版本號引用 | AtomicStampedReference, AtomicMarkableReference | 解決 ABA 問題 |
| 累加器 | LongAdder, DoubleAdder, LongAccumulator | 高併發計數器,分段累加降低 CAS 衝突 (JDK 8+) |
public final int getAndIncrement() {
for (;;) { // 自旋循環
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) // CAS 嘗試更新
return current;
}
}
valueOffset 通過 Unsafe.objectFieldOffset 獲取字段在對象內存中的偏移量,CAS 直接操作該偏移地址。
線程 1 讀取值 A,準備 CAS 更新為 B。線程 2 將 A→B→A。線程 1 CAS 時發現值仍為 A,誤以為未被修改,更新成功,但實際狀態已發生變化。對於單純計數器無影響,但對於節點引用、狀態機等場景會導致邏輯錯誤。
內部維護 Pair<V> { reference, stamp },每次更新遞增 stamp(時間戳/版本號)。CAS 同時比較引用與 stamp。
AtomicStampedReference money = new AtomicStampedReference<>(19, 0);
// 充值線程:余額 < 20 才充值,且每次操作 stamp+1
int stamp = money.getStamp();
if (money.compareAndSet(19, 39, stamp, stamp + 1)) { ... }
// 消費線程:同樣檢查 stamp
即使值 19→39→19,stamp 已從 0→1→2,第二次 CAS 會因 stamp 不匹配而失敗。
僅需一個 boolean 標記(如「已邏輯刪除」),不需要完整版本號。
compareAndSwapInt/Long/Object:CAS 操作。getAndAddInt/Long:原子增減。putOrderedInt/Long:有序寫入(釋放語義,弱於 volatile)。park/unpark:線程阻塞/喚醒,LockSupport 底層依賴。sun.misc.Unsafe 非標準 API,JDK 9+ 模塊化限制訪問,建議優先使用 VarHandle (JDK 9+) 或原子類封裝。// 靜態字段 VarHandle
private static final VarHandle COUNTER;
static {
try {
COUNTER = MethodHandles.lookup()
.findVarHandle(Counter.class, "count", int.class);
} catch (Exception e) { throw new ExceptionInInitializerError(e); }
}
// 使用
COUNTER.getAndAdd(this, 1); // 原子增
COUNTER.compareAndSet(this, 0, 1); // CAS
VarHandle 提供標準化、模塊化友好的底層內存操作 API,支援多種內存排序模式(plain, opaque, acquire/release, volatile)。
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 單一計數器,中低併發 | AtomicLong | 簡單、語義清晰 |
| 高併發計數器(統計、限流) | LongAdder | 分段累加,CAS 衝突極低,吞吐量碾壓 AtomicLong |
| 對象引用原子更新 | AtomicReference | 泛型支援、無 ABA 風險場景 |
| 無鎖數據結構節點(鏈表、隊列) | AtomicStampedReference | 解決 ABA,保證線性一致性 |
| 現有類字段賦予原子性 | Atomic*FieldUpdater | 無侵入、字段需 volatile |
無鎖編程基於硬件 CAS 指令,通過樂觀重試實現高併發下的優異性能。Java 提供豐富的原子類族覆蓋各類場景,核心在於理解 CAS 語義、ABA 問題、自旋開銷、內存可見性。實際開發中,優先使用 LongAdder 等高級累加器、ConcurrentHashMap 等並發容器,僅在構建自定義無鎖數據結構時直接操作 AtomicReference/VarHandle。