📌 一、什麼是「原子性變數」與 CAS?

在多執行緒環境下,多個執行緒若同時修改同一個變數,容易發生資料競爭。

Java 提供 java.util.concurrent.atomic 套件,裡面有許多「原子操作」的變數類型,例如:

  • AtomicInteger
  • AtomicLong
  • AtomicReference

這些類別提供 非阻塞(non-blocking) 的操作方式,底層就是使用 CAS(Compare-And-Swap,比較並交換) 實作的。


⚙️ 二、CAS 的核心邏輯

CAS 是一種樂觀鎖策略,基本流程如下:

  1. 讀取目前值
  2. 比對是否與預期值一致
  3. 若一致,就更新為新值
  4. 若不一致,就放棄(重試或結束)

✅ 優點:不用 synchronized 就能寫出安全的多執行緒操作

❌ 缺點:高併發時可能需要一直 retry,自旋過多會浪費 CPU

💡 三、實際範例:用 CAS 實作「無鎖計數器」

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import java.util.concurrent.atomic.AtomicInteger;

public class CasCounter {
private final AtomicInteger counter = new AtomicInteger(0);

public void increment() {
while (true) {
int oldValue = counter.get(); // 1. 取得目前值
int newValue = oldValue + 1; // 2. 計算新值
if (counter.compareAndSet(oldValue, newValue)) {
// 3. 嘗試更新:若目前值 == 舊值,就設為新值
break;
}
// 4. 如果失敗(可能被其他執行緒改掉了),就重試
}
}

public int getValue() {
return counter.get();
}

public static void main(String[] args) throws InterruptedException {
CasCounter counter = new CasCounter();

Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});

Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) counter.increment();
});

t1.start(); t2.start();
t1.join(); t2.join();

System.out.println("Final count: " + counter.getValue());// 預期結果為 2000
}
}

🔍 這裡的 compareAndSet(old, new) 就是 CAS 的操作:

如果 counter 的值還是等於 old,就設為 new,否則什麼都不做。

🚧 四、CAS 的常見問題與限制

問題類型 說明
ABA 問題 值從 A ➝ B ➝ A,CAS 會誤判沒變。可用 AtomicStampedReference 解決。
自旋太久 高併發下 CAS 一直失敗,浪費 CPU(spin loop)
無法批次更新 只能針對單一變數操作,無法同時修改多個變數

📚 補充用法(其他類型)

1
2
3
4
5
AtomicLong longValue = new AtomicLong(100);
longValue.addAndGet(5); // 加 5,變成 105

AtomicReference<String> str = new AtomicReference<>("Hello");
str.compareAndSet("Hello", "World"); // 成功替換