CAS (Compare-And-Swap,比較並交換) 是現代並發編程的基石,Java 5+ 引入 java.util.concurrent.atomic 包將處理器級 CAS 指令暴露給開發者,實現了無鎖線程安全。本文將從原理、場景、源碼、實戰四個維度深度剖析 CAS 技術。
三個操作數:
原子語義:if (V == A) { V = B; return true; } else { return false; } — 整個判斷與更新過程不可分割,由 CPU 硬體指令保證 (x86: LOCK CMPXCHG,ARM: LDREX/STREX)。
// 非線程安全:檢查與操作非原子
class MyLock {
private boolean locked = false;
public boolean lock() {
if (!locked) { locked = true; return true; } // 競態窗口!
return false;
}
}
多線程並發時,線程 A、B 同時讀到 locked=false,雙雙進入 if 設置為 true,導致鎖失效。
public synchronized boolean lock() { // 重量級鎖,上下文切換開銷大
if (!locked) { locked = true; return true; }
return false;
}
雖然解決線程安全,但引入悲觀鎖機制:線程阻塞、內核態切換、優先級反轉等性能隱患。
import java.util.concurrent.atomic.AtomicBoolean;
class MyLock {
private final AtomicBoolean locked = new AtomicBoolean(false);
public boolean lock() {
// compareAndSet(expected, update) 返回 boolean
return locked.compareAndSet(false, true); // 原子 CAS 操作
}
public void unlock() {
locked.set(false); // volatile 寫入,保證可見性
}
}
優勢:無線程阻塞、無上下文切換、用戶態完成同步、性能隨併發度線性擴展 (低競爭場景)。
// AtomicInteger
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// Unsafe (JVM 內部實現)
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset); // volatile 讀取最新值
} while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS 自旋重試
return v;
}
valueOffset:字段在對象內存中的偏移量 (對象頭 + 實例字段佈局)getIntVolatile:內存屏障語義,禁止指令重排,強制從主內存讀取compareAndSwapInt:JVM 內部函數,映射為 CPU LOCK CMPXCHG 指令| 問題 | 描述 | 解決方案 |
|---|---|---|
| ABA 問題 | 值 A→B→A,CAS 誤以為未變更 | AtomicStampedReference / AtomicMarkableReference 引入版本戳/標記位 |
| 自旋 CPU 佔用高 | 長期 CAS 失敗佔用 CPU | JDK 8+ LongAdder 分段計數器;適度 Thread.yield() / LockSupport.parkNanos() |
| 只能保證單變量原子 | 多變量組合操作非原子 | 封裝為單一對象用 AtomicReference;或用鎖/STM (Software Transactional Memory) |
public class CounterBenchmark {
static final int THREADS = 16;
static final int ITERATIONS = 1_000_000;
public static void main(String[] args) throws InterruptedException {
// 1. synchronized
testSync(() -> new SyncCounter());
// 2. ReentrantLock
testSync(() -> new LockCounter());
// 3. AtomicLong
testSync(() -> new AtomicCounter());
// 4. LongAdder (JDK 8+ 推薦高併發)
testSync(() -> new LongAdderCounter());
}
static void testSync(Supplier supplier) throws InterruptedException {
Counter c = supplier.get();
long start = System.nanoTime();
ExecutorService es = Executors.newFixedThreadPool(THREADS);
for (int i = 0; i < THREADS; i++)
es.execute(() -> { for (int j = 0; j < ITERATIONS; j++) c.increment(); });
es.shutdown(); es.awaitTermination(1, TimeUnit.MINUTES);
System.out.printf("%s: %,d ns, value=%,d%n", c.getClass().getSimpleName(),
System.nanoTime() - start, c.get());
}
}
interface Counter { void increment(); long get(); }
class SyncCounter implements Counter { private long v; public synchronized void increment() { v++; } public long get() { return v; } }
class LockCounter implements Counter { private long v; private final Lock l = new ReentrantLock(); public void increment() { l.lock(); try { v++; } finally { l.unlock(); } } public long get() { return v; } }
class AtomicCounter implements Counter { private final AtomicLong v = new AtomicLong(); public void increment() { v.incrementAndGet(); } public long get() { return v.get(); } }
class LongAdderCounter implements Counter { private final LongAdder v = new LongAdder(); public void increment() { v.increment(); } public long get() { return v.sum(); } }
典型輸出 (高併發場景):
SyncCounter: 1,200,000,000 ns, value=16,000,000
LockCounter: 1,150,000,000 ns, value=16,000,000
AtomicCounter: 450,000,000 ns, value=16,000,000
LongAdderCounter: 120,000,000 ns, value=16,000,000 ← 分段無鎖,性能碾壓
LongAdder/DoubleAdderAtomicStampedReferenceAtomicBooleanAtomicReferenceCAS 以樂觀鎖思想配合硬體原子指令,實現了用戶態無阻塞同步,是 JUC 包高性能並發組件的基石。理解 Unsafe、volatile 內存語義、ABA 問題與 LongAdder 分段思想,能幫助開發者在高併發系統中做出正確的同步策略選型。實際開發中,優先考慮成熟組件 (ConcurrentHashMap、LongAdder、CompletableFuture) 而非手寫 CAS 邏輯,除非在構建基礎設施級組件。