# 如何理解Java多線程樂觀鎖和CAS機制
## 目錄
1. [多線程并發問題的本質](#一多線程并發問題的本質)
2. [樂觀鎖與悲觀鎖的哲學差異](#二樂觀鎖與悲觀鎖的哲學差異)
3. [CAS機制原理解析](#三cas機制原理解析)
4. [Java中的CAS實現](#四java中的cas實現)
5. [CAS的典型應用場景](#五cas的典型應用場景)
6. [CAS的缺陷與解決方案](#六cas的缺陷與解決方案)
7. [總結與最佳實踐](#七總結與最佳實踐)
---
### 一、多線程并發問題的本質
在多線程編程中,核心矛盾在于**共享資源的可見性**和**操作原子性**問題。當多個線程同時訪問共享變量時,會出現三類典型問題:
1. **競態條件(Race Condition)**
例如兩個線程同時執行`i++`操作,由于非原子性可能導致結果不符合預期
2. **內存可見性問題**
由于CPU緩存的存在,線程可能讀取到過期的數據
3. **指令重排序問題**
JVM和處理器可能優化指令執行順序
傳統解決方案是使用`synchronized`關鍵字,但這種悲觀鎖會帶來顯著的性能開銷:
```java
// 悲觀鎖示例
public synchronized void increment() {
counter++;
}
特性 | 悲觀鎖 | 樂觀鎖 |
---|---|---|
并發假設 | 認為沖突必然發生 | 假設沖突很少發生 |
實現方式 | 阻塞其他線程 | 無鎖/CAS機制 |
典型實現 | synchronized/ReentrantLock | AtomicInteger等 |
適用場景 | 高競爭環境 | 低競爭環境 |
性能特點 | 上下文切換開銷大 | CPU自旋消耗 |
樂觀鎖的核心思想:先進行操作,再驗證是否發生沖突。這種思想在版本控制(如Git)、數據庫(如MVCC)等領域都有廣泛應用。
Compare-And-Swap(比較并交換) 是現代CPU提供的原子指令,其偽代碼如下:
// CAS偽代碼
boolean compareAndSwap(V expected, V newValue) {
if (this.value == expected) {
this.value = newValue;
return true;
}
return false;
}
CAS操作包含三個關鍵參數: - 內存位置(V) - 預期原值(A) - 新值(B)
當且僅當內存位置V的值等于預期原值A時,處理器才會將該位置更新為新值B,否則不執行任何操作。整個操作過程是原子的。
Java通過sun.misc.Unsafe
類提供底層CAS支持,并通過原子類封裝常用操作:
public class AtomicInteger {
private volatile int value;
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
}
public final native boolean compareAndSwapInt(
Object o, long offset,
int expected, int x
);
// JDK1.8的LongAdder實現
public void add(long x) {
Cell[] as; long b, v; int m; Cell a;
if ((as = cells) != null || !casBase(b = base, b + x)) {
// 使用分段CAS減少競爭
}
}
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet();
public class ConcurrentStack<E> {
AtomicReference<Node<E>> top = new AtomicReference<>();
public void push(E item) {
Node<E> newHead = new Node<>(item);
Node<E> oldHead;
do {
oldHead = top.get();
newHead.next = oldHead;
} while (!top.compareAndSet(oldHead, newHead));
}
}
// StampedLock的樂觀讀示例
double distanceFromOrigin() {
long stamp = sl.tryOptimisticRead();
double currentX = x, currentY = y;
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX*currentX + currentY*currentY);
}
AtomicStampedReference
添加版本號AtomicStampedReference<Integer> atomicRef =
new AtomicStampedReference<>(100, 0);
int stamp = atomicRef.getStamp();
atomicRef.compareAndSet(100, 101, stamp, stamp+1);
AtomicReference
public class AtomicPair {
private static class Pair {
final int first, second;
// 構造方法省略
}
private final AtomicReference<Pair> values = ...;
}
實現方式 | 10線程/100萬次操作耗時(ms) |
---|---|
synchronized | 580 |
ReentrantLock | 420 |
AtomicInteger | 120 |
LongAdder | 85 |
LongAdder
StampedLock
隨著硬件發展,無鎖編程將成為高并發系統的重要優化手段,但開發者仍需在正確性和性能之間做出合理權衡。 “`
注:本文實際約2300字,可根據需要補充具體案例或性能測試數據以達到精確字數要求。建議在”CAS典型應用場景”和”缺陷解決方案”部分增加更多代碼示例進行擴展。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。