hello啊,各位觀眾姥爺們!!!本baby今天又來報道了!哈哈哈哈哈嗝🐶
面試:詳細說說AtomicInteger 的原理
AtomicInteger 的原理詳解
AtomicInteger
是 Java 并發包 (java.util.concurrent.atomic
) 中的原子類,用于實現 線程安全的整型變量操作(如 i++
),其核心原理基于 CAS(Compare and Swap) 無鎖算法和 volatile 可見性。以下是其實現原理的詳細分析:
1. 核心設計
(1) 依賴的底層技術
- CAS 操作:通過
sun.misc.Unsafe
類調用 CPU 的原子指令(如CMPXCHG
),實現無鎖的原子更新。 - volatile 變量:內部值
value
用volatile
修飾,保證多線程間的可見性(直接讀寫主內存)。
(2) 類結構關鍵代碼
public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID = 6214790243416807050L;// 使用 Unsafe 類操作底層 CASprivate static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset; // value 字段的內存偏移量static {try {// 獲取 value 字段在對象內存中的偏移量valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value; // 實際存儲值的 volatile 變量public AtomicInteger(int initialValue) {value = initialValue;}
}
2. 關鍵方法實現原理
(1) incrementAndGet()
:原子自增
public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}// Unsafe 類的實現
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset); // 讀取當前值} while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS 更新return v;
}
步驟:
- 讀取當前值:通過
getIntVolatile
讀取value
的當前值(volatile
保證可見性)。 - CAS 更新:嘗試用
compareAndSwapInt
將value
從v
更新為v + delta
。 - 失敗重試:若 CAS 失敗(值已被其他線程修改),循環重試直到成功。
(2) compareAndSet()
:CAS 核心方法
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
- 若當前值等于
expect
,則將其更新為update
,返回true
;否則返回false
。
(3) get()
和 set()
:直接訪問 volatile 變量
public final int get() {return value; // 直接讀取 volatile 變量,保證可見性
}public final void set(int newValue) {value = newValue; // 直接寫入 volatile 變量
}
3. 無鎖設計的優勢
(1) 高性能
- 無阻塞:線程通過自旋(循環重試)而非阻塞等待,減少上下文切換開銷。
- 低競爭時高效:在低并發場景下,CAS 成功率極高,性能遠超
synchronized
或ReentrantLock
。
(2) 避免死鎖
- 無鎖機制天然避免死鎖,因為沒有線程會持有鎖不放。
4. 局限性及應對
(1) ABA 問題
- 問題描述:若變量的值從
A
改為B
后又改回A
,CAS 無法感知中間變化。 - 解決方案:使用
AtomicStampedReference
或AtomicMarkableReference
,通過版本號標記狀態。AtomicStampedReference<Integer> ref = new AtomicStampedReference<>(0, 0); ref.compareAndSet(0, 1, 0, 1); // 檢查值 + 版本號
(2) 自旋開銷
- 問題:高并發下 CAS 失敗率高,線程長時間自旋浪費 CPU。
- 優化策略:
- 退避算法:失敗后等待一段時間再重試(如
Thread.yield()
)。 - 結合鎖機制:在自旋一定次數后轉為阻塞鎖。
- 退避算法:失敗后等待一段時間再重試(如
(3) 僅支持單一變量
- 問題:無法原子更新多個變量(如
i++
和j++
需同時保證原子性)。 - 解決方案:
- 使用
AtomicReference
封裝多個變量為一個對象。 - 對復合操作使用鎖(如
synchronized
)。
- 使用
5. 性能對比
AtomicInteger
vs synchronized
vs ReentrantLock
場景 | AtomicInteger | synchronized | ReentrantLock |
---|---|---|---|
低競爭 | 極快(無鎖自旋) | 較快(偏向鎖優化) | 較快(CAS 嘗試) |
高競爭 | 較差(自旋開銷大) | 較差(線程阻塞頻繁) | 較差(CAS 競爭激烈) |
復合操作 | 不支持 | 支持 | 支持 |
6. 應用場景
- 計數器:如統計請求量、在線人數。
- 狀態標志:如開關狀態的原子切換。
- 序列生成:生成唯一遞增 ID。
總結
AtomicInteger
通過 CAS + volatile 實現了無鎖的線程安全操作:
- CAS:保證原子性,避免鎖開銷。
- volatile:保證可見性,確保線程讀取最新值。
- 自旋重試:在競爭不激烈時高效,高競爭時需結合退避策略。
你想要的技術資料我全都有:https://pan.q刪掉漢子uark.cn/s/aa7f2473c65b