十五年前多處理器系統昂貴且稀有,如今無處不在。要榨乾多核性能,必須編寫多線程程式,但單靠分割工作不夠——必須確保線程大多數時間在工作而非等待鎖。本文探討 JDK 5.0 引入的 java.util.concurrent.atomic 包如何利用硬件級原語實現高性能無鎖併發。
線程間若無協調幾乎無法真正並行。傳統方案是同步/鎖,保證獨占訪問與內存可見性。但鎖競爭激烈時吞吐量暴跌:JVM 花費更多時間調度線程、管理等待隊列,而非做實際工作。更糟的是,持有鎖的線程若被延遲(頁錯誤、調度延遲),所有等待該鎖的線程全部阻塞。
現代處理器(x86 cmpxchg、PowerPC lwarx/stwcx、MIPS ll/sc)提供 CAS 指令:
CAS(內存位置 V, 預期值 A, 新值 B):
若 V == A 則 V = B,返回 true
否則不修改 V,返回 false
CAS 允許「樂觀」執行讀-修改-寫:讀取值 A,計算新值 B,嘗試 CAS。若失敗說明有干擾,重試或採取其他行動。
基於鎖:
public class SynchronizedCounter {
private int value;
public synchronized int increment() { return ++value; }
}
基於 CAS(無鎖):
public class CasCounter {
private SimulatedCAS value; // 實際用 AtomicInteger
public int increment() {
int oldValue = value.getValue();
while (value.compareAndSwap(oldValue, oldValue + 1) != oldValue)
oldValue = value.getValue();
return oldValue + 1;
}
}
無鎖算法保證:無阻塞(某個線程總能進展)或 無等待(每個線程有限步驟內完成)。避免優先級反轉、死鎖,競爭開銷極低,協調粒度精細到單個變量。
java.util.concurrent.atomic 提供 9 種原子類型:AtomicInteger、AtomicLong、AtomicReference、AtomicBoolean、原子陣列、原子標記/戳記引用。它們可視為 volatile 的泛化:支援原子條件更新(CAS),讀寫語義等同 volatile。
對比同步、ReentrantLock、公平鎖與 AtomicLong:
// 同步版
public synchronized int nextInt(int n) {
int s = seed;
seed = Util.calculateNext(seed);
return s % n;
}
// AtomicLong 版
public int nextInt(int n) {
for (;;) {
int s = seed.get();
int nexts = Util.calculateNext(s);
if (seed.compareAndSet(s, nexts)) return s % n;
}
}

圖表顯示:AtomicLong 在高競爭下顯著優於 ReentrantLock,後者又優於同步。細粒度硬件協調帶來更高吞吐量。
java.util.concurrent 幾乎所有類直接或間接使用原子變量ConcurrentLinkedQueue 用原子變量實現無等待算法ConcurrentHashMap 使用 ReentrantLock,其內部隊列又靠原子變量維護開發者可直接用原子變量替代共享計數器、序號生成器等獨立共享變量的同步實現。
JDK 5.0 通過暴露底層協調原語與公共原子類,使 Java 首次能編寫無等待、無鎖算法。java.util.concurrent 建立在這些基石上,提供顯著可伸縮性優勢。即使你不直接使用原子變量,也應為其存在喝彩——它們是現代高性能併發框架的基石。