hello啊,各位觀眾姥爺們!!!本baby今天又來報道了!哈哈哈哈哈嗝🐶
面試官:CAS都有哪些問題?如何解決?
CAS 的問題及解決方案
CAS(Compare and Swap)是一種高效的無鎖并發機制,但在實際使用中需注意以下問題及其解決方案:
1. ABA 問題
問題描述
- 場景:線程 1 讀取共享變量值為
A
,此時線程 2 將值修改為B
,后又改回A
。 - 后果:線程 1 執行 CAS 時認為值未被修改(仍為
A
),操作成功,但實際變量已被其他線程修改過,可能導致邏輯錯誤。
解決方案
- 版本號機制:為變量附加一個版本號(或時間戳),每次修改遞增版本號,CAS 需同時檢查值和版本號。
- Java 實現:
AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(100, 0);// 更新時檢查值 + 版本號 int oldStamp = ref.getStamp(); int newValue = 200; ref.compareAndSet(100, newValue, oldStamp, oldStamp + 1);
2. 自旋開銷(CPU 資源浪費)
問題描述
- 場景:高并發下多個線程頻繁競爭同一變量,CAS 失敗后線程會循環重試(自旋),導致 CPU 資源浪費。
- 后果:大量線程自旋時,CPU 利用率飆升,系統吞吐量下降。
解決方案
- 自旋優化策略:
- 限制自旋次數:設定最大自旋次數,超過后改用鎖或阻塞。
- 退避算法:每次失敗后增加等待時間(如指數退避)。
- 示例代碼(退避):
int maxRetries = 10; int retries = 0; while (!atomicInt.compareAndSet(oldValue, newValue)) {if (retries++ > maxRetries) {// 退化為鎖機制synchronized(lock) { ... }break;}Thread.sleep(1 << retries); // 指數退避 }
3. 只能保證單個變量的原子性
問題描述
- 場景:需對多個共享變量進行原子更新時,CAS 無法直接實現。
- 例如:轉賬操作需同時修改“賬戶 A 余額”和“賬戶 B 余額”。
解決方案
- 合并變量:將多個變量封裝為一個對象,用
AtomicReference
原子更新整個對象。 - 鎖機制:對復合操作使用鎖(如
synchronized
或ReentrantLock
)。 - 示例代碼(合并變量):
class AccountPair {int balanceA;int balanceB; }AtomicReference<AccountPair> pairRef = new AtomicReference<>(new AccountPair());// 原子更新 AccountPair oldPair = pairRef.get(); AccountPair newPair = new AccountPair(oldPair.balanceA - 100, oldPair.balanceB + 100); pairRef.compareAndSet(oldPair, newPair);
4. 公平性問題
問題描述
- 場景:CAS 是非公平的,無法保證等待時間最長的線程優先執行。
- 后果:高競爭下某些線程可能長期無法獲取資源(饑餓現象)。
解決方案
- 隊列調度:結合隊列機制(如 AQS 的 CLH 隊列),按 FIFO 順序分配資源。
- 公平鎖:使用
ReentrantLock
的公平模式,確保先到先得。
5. 硬件平臺限制
問題描述
- 場景:某些硬件(如舊 CPU 或嵌入式設備)不支持 CAS 指令。
- 后果:無法直接使用 CAS,需依賴軟件模擬(性能較差)。
解決方案
- 軟件模擬:通過鎖或操作系統提供的原子 API 實現類似功能。
- 平臺適配:使用高層并發庫(如 Java 的
java.util.concurrent
),屏蔽底層差異。
總結
問題 | 解決方案 | 適用場景 |
---|---|---|
ABA 問題 | 版本號(AtomicStampedReference ) | 需嚴格檢查變量歷史狀態 |
自旋開銷 | 退避算法、限制自旋次數、改用鎖 | 高競爭場景 |
多變量原子性 | 合并變量或使用鎖 | 復合操作需求 |
公平性 | 隊列調度或公平鎖 | 需避免線程饑餓 |
硬件限制 | 軟件模擬或高層并發庫 | 不支持 CAS 的平臺 |
你想要的技術資料我全都有:https://pan.q刪掉漢子uark.cn/s/aa7f2473c65b