非阻塞算法屬於並發算法,不使用鎖,而是依賴硬件原生原子指令(如 CAS)實現線程協作。優勢:避免死鎖、優先級反轉;高併發下吞吐率通常優於鎖。缺點:實現極其困難,需處理 ABA 問題、指令重排等。
| 維度 | 悲觀鎖 | 樂觀鎖 (CAS) |
|---|---|---|
| 假設 | 衝突經常發生,必須加鎖 | 衝突罕見,先嘗試更新,失敗再重試 |
| 實現 | synchronized, ReentrantLock | CAS 指令 (Unsafe.compareAndSwapInt) |
| 優點 | 簡單、強一致性 | 無阻塞、無死鎖、性能隨併發度增長 |
| 缺點 | 上下文切換開銷、死鎖風險 | 高衝突下重試消耗 CPU、ABA 問題 |
三個操作數:內存值 V、預期值 A、新值 B。
原子操作:if (V == A) V = B; return old V;
典型模式:
V 為 A。A 計算新值 B。CAS(V, A, B):若期間 V 未被修改則更新成功,否則重試。
public class SimulatedCAS {
private int value;
public synchronized int getValue() { return value; }
public synchronized int compareAndSwap(int expected, int newValue) {
int old = value;
if (value == expected) value = newValue;
return old;
}
}
public class CasCounter {
private SimulatedCAS value = new SimulatedCAS();
public int getValue() { return value.getValue(); }
public int increment() {
int old = value.getValue();
while (value.compareAndSwap(old, old + 1) != old)
old = value.getValue();
return old + 1;
}
}
JDK 5.0+ java.util.concurrent.atomic 包提供 12 個原子類,四大類:
| 類別 | 代表類 | 適用場景 |
|---|---|---|
| 標量 | AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference | 單一變量原子更新 |
| 字段更新器 | AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater | 非原子字段原子化更新 |
| 數組 | AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray | 數組元素原子更新 |
| 復合 | AtomicMarkableReference, AtomicStampedReference | 解決 ABA 問題(帶版本戳/標記) |
AtomicInteger cnt = new AtomicInteger(0); cnt.get(); // 獲取值 cnt.set(10); // 設置值 cnt.getAndSet(20); // 設置並返回舊值 cnt.getAndIncrement(); // i++ 返回舊值 cnt.incrementAndGet(); // ++i 返回新值 cnt.getAndAdd(5); // +=5 返回舊值 cnt.compareAndSet(10, 15); // CAS 期望 10 更新為 15
場景:線程 1 讀取 V=A,線程 2 將 V 改為 B 再改回 A,線程 1 CAS 成功但實際語義已變。
解決:使用 AtomicStampedReference(帶整數戳記)或 AtomicMarkableReference(布爾標記)。
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0); // 更新時檢查戳記 ref.compareAndSet(100, 200, stamp, stamp+1);
AtomicInteger/Long/Boolean/Reference。AtomicReference。LongAdder (JDK 8) 分段累加,性能優於 AtomicLong。Thread.onSpinWait() (JDK 9+)。