文章目錄
- 一、Atomic 類的核心原理
- 二、常見 Atomic 類及用法
- 1. 基本類型原子類
- (1)`AtomicInteger`(原子更新 int)
- (2)`AtomicLong`(原子更新 long)
- (3)`AtomicBoolean`(原子更新 boolean)
- 2. 引用類型原子類
- (1)`AtomicReference<V>`(原子更新對象引用)
- (2)`AtomicStampedReference<V>`(解決 ABA 問題)
- (3)`AtomicMarkableReference<V>`(標記是否修改)
- 3. 數組類型原子類
- (1)`AtomicIntegerArray`(原子更新 int 數組)
- (2)`AtomicLongArray` 和 `AtomicReferenceArray`
- 4. 字段更新器(FieldUpdaters)
- (1)`AtomicIntegerFieldUpdater`(更新對象的 int 字段)
- 三、Atomic 類的適用場景
- 四、局限性
- 總結
Java 中的
java.util.concurrent.atomic
包提供了一組原子操作類,用于實現多線程環境下的無鎖線程安全編程。這些類基于硬件提供的CAS(Compare-And-Swap,比較并交換) 指令實現,避免了傳統鎖機制的性能開銷,適用于簡單值的并發更新場景(如計數器、標志位等)。
一、Atomic 類的核心原理
Atomic 類的線程安全依賴 CAS 操作,其基本邏輯如下:
- 讀取當前內存中的值(
V
)。- 計算目標值(
N
,基于當前值的更新結果)。- 用 CAS 指令比較內存中的值是否仍為
V
:
- 若是,將內存值更新為
N
,操作成功。- 若否(被其他線程修改),不做操作,重試或放棄(通常會循環重試)。
CAS 是硬件級別的原子操作,無需加鎖即可保證操作的原子性,因此性能優于
synchronized
或ReentrantLock
。
二、常見 Atomic 類及用法
atomic
包提供了針對不同數據類型的原子類,可分為以下幾類:
1. 基本類型原子類
用于對
int
、long
等基本類型進行原子更新。
(1)AtomicInteger
(原子更新 int)
核心方法:
getAndIncrement()
:自增 1,返回更新前的值(類似i++
)。incrementAndGet()
:自增 1,返回更新后的值(類似++i
)。getAndAdd(int delta)
:增加指定值,返回更新前的值。compareAndSet(int expect, int update)
:若當前值等于expect
,則更新為update
,返回是否成功。
示例:
AtomicInteger count = new AtomicInteger(0);// 自增(線程安全)
count.incrementAndGet(); // 結果為 1// 條件更新
boolean success = count.compareAndSet(1, 100); // 成功,返回 true,值變為 100
(2)AtomicLong
(原子更新 long)
用法與
AtomicInteger
類似,用于long
類型的原子操作。在 32 位 JVM 上,long
的非原子操作可能存在拆分風險,AtomicLong
可保證其原子性。
(3)AtomicBoolean
(原子更新 boolean)
用于原子更新布爾值,常用
compareAndSet
實現線程安全的開關控制:
AtomicBoolean flag = new AtomicBoolean(false);// 若當前為 false,則更新為 true
if (flag.compareAndSet(false, true)) {System.out.println("執行初始化操作");
}
2. 引用類型原子類
用于原子更新對象引用,支持更復雜的并發場景。
(1)AtomicReference<V>
(原子更新對象引用)
可對任意對象引用進行原子操作,例如原子更新用戶對象:
class User {String name;public User(String name) { this.name = name; }
}AtomicReference<User> userRef = new AtomicReference<>(new User("Alice"));// 原子更新用戶(若當前是 Alice,則改為 Bob)
User oldUser = new User("Alice");
User newUser = new User("Bob");
boolean swapped = userRef.compareAndSet(oldUser, newUser); // 成功,返回 true
(2)AtomicStampedReference<V>
(解決 ABA 問題)
AtomicReference
存在 ABA 問題(值從 A 變為 B 再變回 A,CAS 會誤認為未修改)。AtomicStampedReference
通過版本號(stamp)解決此問題,每次更新時版本號遞增:
// 初始化:值為 "A",版本號為 0
AtomicStampedReference<String> ref = new AtomicStampedReference<>("A", 0);int[] stampHolder = new int[1];
String current = ref.get(stampHolder); // current = "A", stampHolder[0] = 0// 只有當值為 "A" 且版本號為 0 時,才更新為 "B",版本號+1
boolean success = ref.compareAndSet("A", "B", 0, 1); // 成功
(3)AtomicMarkableReference<V>
(標記是否修改)
與
AtomicStampedReference
類似,但用布爾值(mark)標記是否被修改,而非版本號,適用于只需判斷“是否被修改”的場景。
3. 數組類型原子類
用于原子更新數組中的元素。
(1)AtomicIntegerArray
(原子更新 int 數組)
int[] arr = {1, 2, 3};
AtomicIntegerArray atomicArr = new AtomicIntegerArray(arr);// 原子更新索引 1 的元素(加 5)
atomicArr.addAndGet(1, 5); // 數組變為 [1, 7, 3]// 條件更新索引 0 的元素
atomicArr.compareAndSet(0, 1, 10); // 索引 0 的值從 1 變為 10
(2)AtomicLongArray
和 AtomicReferenceArray
分別用于原子更新
long
數組和對象引用數組,用法與AtomicIntegerArray
類似。
4. 字段更新器(FieldUpdaters)
用于原子更新對象的非靜態字段(需字段可見性為
volatile
),無需修改類定義即可實現原子操作。
(1)AtomicIntegerFieldUpdater
(更新對象的 int 字段)
示例:
class Student {volatile int score; // 必須是 volatile 修飾的非靜態字段public Student(int score) { this.score = score; }
}// 創建更新器(指定類和字段名)
AtomicIntegerFieldUpdater<Student> updater = AtomicIntegerFieldUpdater.newUpdater(Student.class, "score");Student student = new Student(80);
// 原子更新 score 字段(加 10)
updater.addAndGet(student, 10); // score 變為 90
類似的還有
AtomicLongFieldUpdater
和AtomicReferenceFieldUpdater
,用于更新long
字段和對象字段。
三、Atomic 類的適用場景
- 計數器:如接口調用次數、并發任務數統計(
AtomicInteger
、AtomicLong
)。- 標志位:如線程安全的開關控制(
AtomicBoolean
)。- 無鎖數據結構:如實現非阻塞隊列、棧等(基于
AtomicReference
)。- 樂觀鎖場景:通過版本號控制(
AtomicStampedReference
)實現并發更新。
四、局限性
- 僅支持簡單操作:適合單一變量的原子更新,復雜邏輯(如多變量聯動)仍需鎖機制。
- 可能導致自旋消耗:CAS 失敗時會循環重試,高并發下可能浪費 CPU 資源。
- 無法解決所有并發問題:如需要互斥的場景(如“檢查-修改-操作”三步原子性),可能仍需鎖。
總結
atomic
包提供的原子類通過 CAS 操作實現了高效的線程安全,適合簡單值的并發更新場景,性能優于傳統鎖機制。但需注意其局限性:僅適用于單一變量操作,復雜場景仍需結合鎖或其他并發工具。實際開發中,應根據業務復雜度選擇合適的并發方案。