| 維度 | 悲觀鎖 | 樂觀鎖 |
|---|---|---|
| 假設 | 衝突常發生,必須預防 | 衝突罕見,先執行再檢測 |
| 機制 | 操作前加鎖(互斥) | 無鎖,CAS/版本號檢測衝突 |
| 衝突處理 | 阻塞等待鎖釋放 | 重試/拋出異常/回滾 |
| 適用場景 | 寫多讀少、高衝突、強一致性要求 | 讀多寫少、低衝突、高吞吐優先 |
| 典型實現 | synchronized, ReentrantLock, 數據庫行鎖 | AtomicInteger, LongAdder, 數據庫版本號 |
// 1. synchronized (對象鎖/類鎖)
public synchronized void transfer(Account from, Account to, int amt) { ... }
// 2. ReentrantLock (顯式鎖,功能更強)
Lock lock = new ReentrantLock();
lock.lock();
try { /* 臨界區 */ } finally { lock.unlock(); }
缺點:線程阻塞/喚醒開銷大(用戶態↔內核態切換)、死鎖風險、鎖競爭導致吞吐下降、優先級反轉。
CAS(V, A, B):內存值 V、預期值 A、新值 B。原子操作:若 V == A 則 V = B 返回 true,否則不修改返回 false。
LOCK CMPXCHG)。// 基本類型原子更新
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // 原子 i++
count.compareAndSet(5, 10); // 手動 CAS
// 長整型高併發優化
LongAdder adder = new LongAdder(); // 內部分段,減少競爭
adder.increment();
long sum = adder.sum();
// 引用類型原子更新
AtomicReference ref = new AtomicReference<>("init");
ref.compareAndSet("init", "updated");
// 數組元素原子更新
AtomicIntegerArray arr = new AtomicIntegerArray(10);
arr.getAndIncrement(0);
線程 1 讀到 A,線程 2 改 A→B→A,線程 1 CAS 成功但實際數據已變。解決:AtomicStampedReference (版本戳) 或 AtomicMarkableReference (布爾標記)。
-- 表增加 version 欄位
UPDATE account SET balance = balance - 100, version = version + 1
WHERE id = 1 AND version = 5; -- 版本匹配才更新
-- affected rows = 0 表示衝突,應用層重試