Java并發編程利器:深入解析13個原子操作類
在多線程并發環境下,保證數據操作的原子性是個常見且關鍵的挑戰。Java從JDK 1.5開始提供了
java.util.concurrent.atomic包,其中包含13個強大的原子操作類,讓我們能夠以無鎖的方式實現線程安全。本文將帶你深入理解這些原子類的原理、API和使用場景。
一、為什么需要原子操作類?
1.1 問題的由來
想象一下這樣的場景:多個線程同時操作同一個銀行賬戶進行取款,如果不加控制,可能會出現什么情況?
// 不安全的計數器示例
class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 這不是原子操作!
}
}
count++看似簡單,實際上包含三個步驟:
- 讀取count的當前值
- 將值加1
- 將新值寫回count
在多線程環境下,這兩個步驟可能被其他線程打斷,導致數據不一致。
1.2 傳統的解決方案及其缺點
傳統做法是使用synchronized關鍵字:
class SynchronizedCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
}
synchronized確實能保證線程安全,但存在以下問題:
- 性能開銷:鎖的獲取和釋放需要代價
- 可能死鎖:不正確的鎖順序可能導致死鎖
- 降低并發性:同一時刻只有一個線程能訪問
1.3 原子操作類的優勢
原子操作類基于CAS(Compare-And-Swap) 機制,提供了:
- 無鎖編程:避免傳統鎖的開銷
- 高性能:在低競爭環境下性能優異
- 無死鎖風險:基于硬件指令,不會產生死鎖
- 高并發:支持多個線程同時操作
二、原子更新基本類型類
2.1 AtomicBoolean - 原子更新布爾類型
使用場景:狀態標志位、開關控制、條件判斷
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
get() |
- | boolean |
獲取當前值 |
set(boolean newValue) |
newValue: 新值 |
void |
設置新值 |
getAndSet(boolean newValue) |
newValue: 新值 |
boolean |
原子性地設置為新值并返回舊值 |
compareAndSet(boolean expect, boolean update) |
expect: 期望值update: 更新值 |
boolean |
如果當前值等于期望值,則原子性地更新 |
lazySet(boolean newValue) |
newValue: 新值 |
void |
最終設置為新值,但不保證立即可見性 |
weakCompareAndSet(boolean expect, boolean update) |
expect: 期望值update: 更新值 |
boolean |
可能更弱的CAS操作,在某些平臺上性能更好 |
import java.util.concurrent.atomic.AtomicBoolean;
/**
* AtomicBoolean示例:用于原子性地更新布爾值
* 典型場景:系統開關、狀態標志等
*/
public class AtomicBooleanDemo {
public static void main(String[] args) {
// 創建AtomicBoolean,初始值為false
AtomicBoolean atomicBoolean = new AtomicBoolean(false);
// get(): 獲取當前值
System.out.println("初始值: " + atomicBoolean.get());
// getAndSet(): 原子性地設置為true,返回舊值
boolean oldValue = atomicBoolean.getAndSet(true);
System.out.println("getAndSet舊值: " + oldValue + ", 新值: " + atomicBoolean.get());
// compareAndSet(): 比較并設置
boolean success = atomicBoolean.compareAndSet(true, false);
System.out.println("CAS操作結果: " + success + ", 當前值: " + atomicBoolean.get());
// lazySet(): 最終會設置,但不保證立即可見性
atomicBoolean.lazySet(true);
System.out.println("lazySet后的值: " + atomicBoolean.get());
// weakCompareAndSet(): 弱版本CAS
boolean weakSuccess = atomicBoolean.weakCompareAndSet(true, false);
System.out.println("弱CAS操作結果: " + weakSuccess + ", 當前值: " + atomicBoolean.get());
}
}
原理分析:
AtomicBoolean內部實際上使用int類型來存儲,0表示false,1表示true。通過compareAndSwapInt來實現原子操作。
2.2 AtomicInteger - 原子更新整型
使用場景:計數器、序列號生成、資源數量控制
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
get() |
- | int |
獲取當前值 |
set(int newValue) |
newValue: 新值 |
void |
設置新值 |
getAndSet(int newValue) |
newValue: 新值 |
int |
原子性地設置為新值并返回舊值 |
compareAndSet(int expect, int update) |
expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement() |
- | int |
原子遞增,返回舊值 |
getAndDecrement() |
- | int |
原子遞減,返回舊值 |
getAndAdd(int delta) |
delta: 增量 |
int |
原子加法,返回舊值 |
incrementAndGet() |
- | int |
原子遞增,返回新值 |
decrementAndGet() |
- | int |
原子遞減,返回新值 |
addAndGet(int delta) |
delta: 增量 |
int |
原子加法,返回新值 |
updateAndGet(IntUnaryOperator) |
operator: 更新函數 |
int |
函數式更新 |
accumulateAndGet(int x, IntBinaryOperator) |
x: 參數operator: 操作函數 |
int |
累積計算 |
import java.util.concurrent.atomic.AtomicInteger;
/**
* AtomicInteger是最常用的原子類之一
* 適用于計數器、ID生成器等需要原子遞增的場景
*/
public class AtomicIntegerDemo {
public static void main(String[] args) {
AtomicInteger atomicInt = new AtomicInteger(0);
// 基礎操作
System.out.println("初始值: " + atomicInt.get());
atomicInt.set(5);
System.out.println("set(5)后: " + atomicInt.get());
// 原子遞增并返回舊值 - 常用于計數
System.out.println("getAndIncrement: " + atomicInt.getAndIncrement()); // 返回5
System.out.println("當前值: " + atomicInt.get()); // 6
// 原子遞減并返回舊值
System.out.println("getAndDecrement: " + atomicInt.getAndDecrement()); // 返回6
System.out.println("當前值: " + atomicInt.get()); // 5
// 原子加法并返回舊值
System.out.println("getAndAdd(10): " + atomicInt.getAndAdd(10)); // 返回5
System.out.println("當前值: " + atomicInt.get()); // 15
// 原子遞增并返回新值
System.out.println("incrementAndGet: " + atomicInt.incrementAndGet()); // 16
// 原子加法并返回結果 - 適合批量增加
int result = atomicInt.addAndGet(10);
System.out.println("addAndGet(10)結果: " + result); // 26
// 比較并設置 - 核心CAS操作
boolean updated = atomicInt.compareAndSet(26, 30);
System.out.println("CAS操作結果: " + updated + ", 當前值: " + atomicInt.get());
// 獲取并設置新值 - 適合重置操作
int previous = atomicInt.getAndSet(40);
System.out.println("getAndSet舊值: " + previous + ", 新值: " + atomicInt.get());
// JDK8新增:函數式更新 - 更靈活的更新方式
atomicInt.updateAndGet(x -> x * 2);
System.out.println("updateAndGet(*2)后的值: " + atomicInt.get()); // 80
// 累積計算
atomicInt.accumulateAndGet(10, (x, y) -> x + y * 2);
System.out.println("accumulateAndGet后的值: " + atomicInt.get()); // 100
}
}
源碼分析:
public final int getAndIncrement() {
// 自旋CAS:循環直到成功
for (;;) {
int current = get(); // 步驟1:獲取當前值
int next = current + 1; // 步驟2:計算新值
if (compareAndSet(current, next)) // 步驟3:CAS更新
return current; // 成功則返回舊值
}
// 如果CAS失敗,說明有其他線程修改了值,循環重試
}
2.3 AtomicLong - 原子更新長整型
使用場景:大數值計數器、統計信息、唯一ID生成
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
get() |
- | long |
獲取當前值 |
set(long newValue) |
newValue: 新值 |
void |
設置新值 |
getAndSet(long newValue) |
newValue: 新值 |
long |
原子性地設置為新值并返回舊值 |
compareAndSet(long expect, long update) |
expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement() |
- | long |
原子遞增,返回舊值 |
getAndDecrement() |
- | long |
原子遞減,返回舊值 |
getAndAdd(long delta) |
delta: 增量 |
long |
原子加法,返回舊值 |
incrementAndGet() |
- | long |
原子遞增,返回新值 |
decrementAndGet() |
- | long |
原子遞減,返回新值 |
addAndGet(long delta) |
delta: 增量 |
long |
原子加法,返回新值 |
updateAndGet(LongUnaryOperator) |
operator: 更新函數 |
long |
函數式更新 |
accumulateAndGet(long x, LongBinaryOperator) |
x: 參數operator: 操作函數 |
long |
累積計算 |
import java.util.concurrent.atomic.AtomicLong;
/**
* AtomicLong用于長整型的原子操作
* 在64位系統中性能與AtomicInteger相當
*/
public class AtomicLongDemo {
public static void main(String[] args) {
AtomicLong atomicLong = new AtomicLong(100L);
System.out.println("初始值: " + atomicLong.get());
// 原子遞增并返回舊值 - 適合序列號生成
System.out.println("getAndIncrement: " + atomicLong.getAndIncrement());
System.out.println("當前值: " + atomicLong.get());
// 原子遞減并返回舊值
System.out.println("getAndDecrement: " + atomicLong.getAndDecrement());
System.out.println("當前值: " + atomicLong.get());
// 原子加法并返回舊值
System.out.println("getAndAdd(50): " + atomicLong.getAndAdd(50L));
System.out.println("當前值: " + atomicLong.get());
// 原子遞增并返回新值
System.out.println("incrementAndGet: " + atomicLong.incrementAndGet());
// 原子加法并返回結果 - 適合統計累加
long newValue = atomicLong.addAndGet(50L);
System.out.println("addAndGet(50)結果: " + newValue);
// 比較并設置
boolean success = atomicLong.compareAndSet(250L, 300L);
System.out.println("CAS操作結果: " + success + ", 當前值: " + atomicLong.get());
// JDK8新增:函數式更新
atomicLong.updateAndGet(x -> x / 2);
System.out.println("updateAndGet(/2)后的值: " + atomicLong.get());
// JDK8新增:累積計算 - 適合復雜的原子計算
atomicLong.accumulateAndGet(100L, (x, y) -> x * y);
System.out.println("accumulateAndGet后的值: " + atomicLong.get());
}
}
性能提示:
在32位系統上,AtomicLong的CAS操作可能需要鎖住總線,性能相對較差。Java 8提供了LongAdder作為高性能替代方案。
三、原子更新數組類
3.1 AtomicIntegerArray - 原子更新整型數組
使用場景:并發計數器數組、桶統計、并行計算
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
length() |
- | int |
返回數組長度 |
get(int i) |
i: 索引 |
int |
獲取指定索引的值 |
set(int i, int newValue) |
i: 索引newValue: 新值 |
void |
設置指定索引的值 |
getAndSet(int i, int newValue) |
i: 索引newValue: 新值 |
int |
原子設置并返回舊值 |
compareAndSet(int i, int expect, int update) |
i: 索引expect: 期望值update: 更新值 |
boolean |
對指定索引進行CAS操作 |
getAndIncrement(int i) |
i: 索引 |
int |
原子遞增指定索引,返回舊值 |
getAndDecrement(int i) |
i: 索引 |
int |
原子遞減指定索引,返回舊值 |
getAndAdd(int i, int delta) |
i: 索引delta: 增量 |
int |
原子加法,返回舊值 |
incrementAndGet(int i) |
i: 索引 |
int |
原子遞增指定索引,返回新值 |
addAndGet(int i, int delta) |
i: 索引delta: 增量 |
int |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* AtomicIntegerArray允許原子地更新數組中的單個元素
* 注意:構造函數會復制傳入的數組,不影響原數組
*/
public class AtomicIntegerArrayDemo {
public static void main(String[] args) {
int[] initialArray = {1, 2, 3, 4, 5};
// 創建原子整型數組,會復制傳入的數組
AtomicIntegerArray atomicArray = new AtomicIntegerArray(initialArray);
System.out.println("數組長度: " + atomicArray.length());
System.out.println("原始數組: " + atomicArray.toString());
// get(): 獲取指定索引的值
System.out.println("索引0的值: " + atomicArray.get(0));
// set(): 設置指定索引的值
atomicArray.set(0, 10);
System.out.println("set(0, 10)后的數組: " + atomicArray.toString());
// getAndSet(): 原子更新指定索引的元素并返回舊值
int oldValue = atomicArray.getAndSet(1, 20);
System.out.println("索引1替換前的值: " + oldValue + ", 數組: " + atomicArray.toString());
// getAndIncrement(): 原子遞增指定索引的元素 - 適合分桶計數
oldValue = atomicArray.getAndIncrement(2);
System.out.println("索引2遞增前值: " + oldValue + ", 數組: " + atomicArray.toString());
// compareAndSet(): 比較并設置特定位置的元素
boolean updated = atomicArray.compareAndSet(3, 4, 40);
System.out.println("索引3 CAS結果: " + updated + ", 數組: " + atomicArray.toString());
// addAndGet(): 原子加法 - 適合累加統計
int newValue = atomicArray.addAndGet(4, 5);
System.out.println("索引4加5后的值: " + newValue + ", 數組: " + atomicArray.toString());
// incrementAndGet(): 原子遞增并返回新值
newValue = atomicArray.incrementAndGet(0);
System.out.println("索引0遞增后的值: " + newValue);
// 重要:原始數組不會被修改
System.out.println("原始數組值未被修改: " + initialArray[0]); // 仍然是1
}
}
設計思想:
AtomicIntegerArray通過復制數組來避免外部修改,每個數組元素的更新都是獨立的原子操作。
3.2 AtomicLongArray - 原子更新長整型數組
使用場景:大數據統計、時間戳數組、大數值桶統計
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
length() |
- | int |
返回數組長度 |
get(int i) |
i: 索引 |
long |
獲取指定索引的值 |
set(int i, long newValue) |
i: 索引newValue: 新值 |
void |
設置指定索引的值 |
getAndSet(int i, long newValue) |
i: 索引newValue: 新值 |
long |
原子設置并返回舊值 |
compareAndSet(int i, long expect, long update) |
i: 索引expect: 期望值update: 更新值 |
boolean |
對指定索引進行CAS操作 |
getAndAdd(int i, long delta) |
i: 索引delta: 增量 |
long |
原子加法,返回舊值 |
addAndGet(int i, long delta) |
i: 索引delta: 增量 |
long |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicLongArray;
/**
* AtomicLongArray提供長整型數組的原子操作
* 適用于需要大數值范圍的并發統計
*/
public class AtomicLongArrayDemo {
public static void main(String[] args) {
long[] initialArray = {100L, 200L, 300L, 400L, 500L};
AtomicLongArray atomicLongArray = new AtomicLongArray(initialArray);
System.out.println("數組長度: " + atomicLongArray.length());
System.out.println("初始數組: " + atomicLongArray.toString());
// 基礎操作
System.out.println("索引0的值: " + atomicLongArray.get(0));
atomicLongArray.set(0, 150L);
System.out.println("set(0, 150)后的數組: " + atomicLongArray.toString());
// 原子更新操作
long oldValue = atomicLongArray.getAndSet(1, 250L);
System.out.println("索引1替換前的值: " + oldValue + ", 數組: " + atomicLongArray.toString());
// 原子加法操作
atomicLongArray.getAndAdd(2, 100L);
System.out.println("索引2加100后的數組: " + atomicLongArray.toString());
// 比較并設置
atomicLongArray.compareAndSet(3, 400L, 450L);
System.out.println("索引3 CAS后的數組: " + atomicLongArray.toString());
// 加法并獲取新值
long newValue = atomicLongArray.addAndGet(4, 200L);
System.out.println("索引4加200后的值: " + newValue);
}
}
3.3 AtomicReferenceArray - 原子更新引用類型數組
使用場景:對象池、緩存數組、并發數據結構
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
length() |
- | int |
返回數組長度 |
get(int i) |
i: 索引 |
E |
獲取指定索引的引用 |
set(int i, E newValue) |
i: 索引newValue: 新引用 |
void |
設置指定索引的引用 |
getAndSet(int i, E newValue) |
i: 索引newValue: 新引用 |
E |
原子設置并返回舊引用 |
compareAndSet(int i, E expect, E update) |
i: 索引expect: 期望引用update: 更新引用 |
boolean |
對指定索引進行CAS操作 |
lazySet(int i, E newValue) |
i: 索引newValue: 新引用 |
void |
延遲設置引用 |
import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* AtomicReferenceArray用于原子更新引用類型數組
* 適用于對象引用需要原子更新的場景
*/
public class AtomicReferenceArrayDemo {
static class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "(" + age + ")";
}
}
public static void main(String[] args) {
Person[] persons = {
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Charlie", 35),
new Person("David", 40)
};
AtomicReferenceArray<Person> atomicArray = new AtomicReferenceArray<>(persons);
System.out.println("數組長度: " + atomicArray.length());
System.out.println("初始數組: ");
for (int i = 0; i < atomicArray.length(); i++) {
System.out.println("索引 " + i + ": " + atomicArray.get(i));
}
// 原子更新引用 - 適合對象替換
Person newPerson = new Person("Eve", 28);
Person oldPerson = atomicArray.getAndSet(1, newPerson);
System.out.println("索引1替換: " + oldPerson + " -> " + atomicArray.get(1));
// 比較并設置引用
boolean success = atomicArray.compareAndSet(2, persons[2], new Person("Frank", 45));
System.out.println("索引2 CAS結果: " + success + ", 新值: " + atomicArray.get(2));
// 延遲設置
atomicArray.lazySet(3, new Person("Grace", 50));
System.out.println("索引3延遲設置后的值: " + atomicArray.get(3));
// 遍歷數組
System.out.println("最終數組狀態:");
for (int i = 0; i < atomicArray.length(); i++) {
System.out.println("索引 " + i + ": " + atomicArray.get(i));
}
}
}
四、原子更新引用類型
4.1 AtomicReference - 原子更新引用類型
使用場景:單例模式、緩存更新、狀態對象替換
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
get() |
- | V |
獲取當前引用 |
set(V newValue) |
newValue: 新引用 |
void |
設置新引用 |
getAndSet(V newValue) |
newValue: 新引用 |
V |
原子設置并返回舊引用 |
compareAndSet(V expect, V update) |
expect: 期望引用update: 更新引用 |
boolean |
CAS操作 |
weakCompareAndSet(V expect, V update) |
expect: 期望引用update: 更新引用 |
boolean |
弱版本CAS |
lazySet(V newValue) |
newValue: 新引用 |
void |
延遲設置引用 |
updateAndGet(UnaryOperator<V>) |
operator: 更新函數 |
V |
函數式更新 |
getAndUpdate(UnaryOperator<V>) |
operator: 更新函數 |
V |
函數式更新并返回舊值 |
accumulateAndGet(V x, BinaryOperator<V>) |
x: 參數operator: 操作函數 |
V |
累積計算 |
import java.util.concurrent.atomic.AtomicReference;
/**
* AtomicReference用于原子更新對象引用
* 解決"先檢查后執行"的競態條件
*/
public class AtomicReferenceDemo {
static class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
public static void main(String[] args) {
AtomicReference<User> atomicUser = new AtomicReference<>();
User initialUser = new User("張三", 25);
atomicUser.set(initialUser);
System.out.println("初始用戶: " + atomicUser.get());
// getAndSet(): 原子更新引用 - 適合緩存更新
User newUser = new User("李四", 30);
User oldUser = atomicUser.getAndSet(newUser);
System.out.println("替換前的用戶: " + oldUser);
System.out.println("當前用戶: " + atomicUser.get());
// compareAndSet(): 比較并設置 - 核心操作
boolean success = atomicUser.compareAndSet(newUser, new User("王五", 35));
System.out.println("CAS操作結果: " + success + ", 當前用戶: " + atomicUser.get());
// weakCompareAndSet(): 弱版本CAS
boolean weakSuccess = atomicUser.weakCompareAndSet(
atomicUser.get(), new User("趙六", 40));
System.out.println("弱CAS操作結果: " + weakSuccess + ", 當前用戶: " + atomicUser.get());
// lazySet(): 延遲設置
atomicUser.lazySet(new User("孫七", 45));
System.out.println("延遲設置后的用戶: " + atomicUser.get());
// JDK8新增:函數式更新
atomicUser.updateAndGet(user -> new User(user.getName() + "_updated", user.getAge() + 1));
System.out.println("函數式更新后的用戶: " + atomicUser.get());
// getAndUpdate(): 函數式更新并返回舊值
User previous = atomicUser.getAndUpdate(user -> new User("周八", 50));
System.out.println("更新前的用戶: " + previous + ", 當前用戶: " + atomicUser.get());
// accumulateAndGet(): 累積計算
atomicUser.accumulateAndGet(new User("吳九", 55),
(old, param) -> new User(old.getName() + "&" + param.getName(),
old.getAge() + param.getAge()));
System.out.println("累積計算后的用戶: " + atomicUser.get());
}
}
典型應用:單例模式的雙重檢查鎖定
class Singleton {
private static final AtomicReference<Singleton> INSTANCE = new AtomicReference<>();
public static Singleton getInstance() {
for (;;) {
Singleton current = INSTANCE.get();
if (current != null) return current;
current = new Singleton();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
}
4.2 AtomicMarkableReference - 帶標記位的原子引用
使用場景:帶狀態的緩存、ABA問題簡單解決方案
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
getReference() |
- | V |
獲取當前引用 |
isMarked() |
- | boolean |
獲取當前標記位 |
get(boolean[] markHolder) |
markHolder: 標記位容器 |
V |
獲取引用和標記位 |
set(V newReference, boolean newMark) |
newReference: 新引用newMark: 新標記 |
void |
設置引用和標記位 |
compareAndSet(V expectedReference, V newReference, boolean expectedMark, boolean newMark) |
expectedReference: 期望引用newReference: 新引用expectedMark: 期望標記newMark: 新標記 |
boolean |
同時比較引用和標記位 |
attemptMark(V expectedReference, boolean newMark) |
expectedReference: 期望引用newMark: 新標記 |
boolean |
嘗試只更新標記位 |
import java.util.concurrent.atomic.AtomicMarkableReference;
/**
* AtomicMarkableReference將引用與一個布爾標記位綁定
* 適用于需要同時更新引用和狀態的場景
*/
public class AtomicMarkableReferenceDemo {
public static void main(String[] args) {
String initialRef = "初始數據";
boolean initialMark = false;
// 創建帶標記位的原子引用
AtomicMarkableReference<String> atomicMarkableRef =
new AtomicMarkableReference<>(initialRef, initialMark);
System.out.println("初始引用: " + atomicMarkableRef.getReference());
System.out.println("初始標記: " + atomicMarkableRef.isMarked());
// get(boolean[]): 同時獲取引用和標記位
boolean[] markHolder = new boolean[1];
String currentRef = atomicMarkableRef.get(markHolder);
System.out.println("當前引用: " + currentRef + ", 當前標記: " + markHolder[0]);
// compareAndSet(): 嘗試同時更新引用和標記位
String newRef = "新數據";
boolean newMark = true;
boolean success = atomicMarkableRef.compareAndSet(
initialRef, newRef, initialMark, newMark);
System.out.println("CAS操作結果: " + success);
System.out.println("新引用: " + atomicMarkableRef.getReference());
System.out.println("新標記: " + atomicMarkableRef.isMarked());
// attemptMark(): 只嘗試更新標記位
boolean markUpdated = atomicMarkableRef.attemptMark(newRef, false);
System.out.println("標記更新結果: " + markUpdated);
System.out.println("最終標記: " + atomicMarkableRef.isMarked());
// set(): 直接設置引用和標記位
atomicMarkableRef.set("最終數據", true);
System.out.println("直接設置后的引用: " + atomicMarkableRef.getReference());
System.out.println("直接設置后的標記: " + atomicMarkableRef.isMarked());
}
}
4.3 AtomicStampedReference - 帶版本號的原子引用
使用場景:解決ABA問題、樂觀鎖實現
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
getReference() |
- | V |
獲取當前引用 |
getStamp() |
- | int |
獲取當前版本號 |
get(int[] stampHolder) |
stampHolder: 版本號容器 |
V |
獲取引用和版本號 |
set(V newReference, int newStamp) |
newReference: 新引用newStamp: 新版本號 |
void |
設置引用和版本號 |
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp) |
expectedReference: 期望引用newReference: 新引用expectedStamp: 期望版本號newStamp: 新版本號 |
boolean |
同時比較引用和版本號 |
attemptStamp(V expectedReference, int newStamp) |
expectedReference: 期望引用newStamp: 新版本號 |
boolean |
嘗試只更新版本號 |
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* AtomicStampedReference通過版本號解決ABA問題
* 每次修改都會增加版本號,確保不會誤判
*/
public class AtomicStampedReferenceDemo {
public static void main(String[] args) {
String initialRef = "數據A";
int initialStamp = 0;
// 創建帶版本號的原子引用
AtomicStampedReference<String> atomicStampedRef =
new AtomicStampedReference<>(initialRef, initialStamp);
System.out.println("初始引用: " + atomicStampedRef.getReference());
System.out.println("初始版本號: " + atomicStampedRef.getStamp());
// get(int[]): 同時獲取引用和版本號
int[] stampHolder = new int[1];
String currentRef = atomicStampedRef.get(stampHolder);
System.out.println("當前引用: " + currentRef + ", 當前版本號: " + stampHolder[0]);
// 模擬ABA問題場景
String newRefB = "數據B";
String newRefA = "數據A"; // 又改回A,但版本號不同
// 第一次更新:A -> B,版本號 0 -> 1
boolean firstUpdate = atomicStampedRef.compareAndSet(
initialRef, newRefB, initialStamp, initialStamp + 1);
System.out.println("第一次更新(A->B)結果: " + firstUpdate);
System.out.println("當前引用: " + atomicStampedRef.getReference());
System.out.println("當前版本號: " + atomicStampedRef.getStamp());
// 第二次更新:B -> A,版本號 1 -> 2
boolean secondUpdate = atomicStampedRef.compareAndSet(
newRefB, newRefA, 1, 2);
System.out.println("第二次更新(B->A)結果: " + secondUpdate);
System.out.println("當前引用: " + atomicStampedRef.getReference());
System.out.println("當前版本號: " + atomicStampedRef.getStamp());
// 嘗試用舊版本號更新(會失敗)- 這就是解決ABA問題的關鍵!
boolean failedUpdate = atomicStampedRef.compareAndSet(
newRefA, "新數據", 0, 1); // 使用舊的版本號0
System.out.println("使用舊版本號更新結果: " + failedUpdate);
System.out.println("引用未被修改: " + atomicStampedRef.getReference());
// attemptStamp(): 只更新版本號
boolean stampUpdated = atomicStampedRef.attemptStamp(newRefA, 3);
System.out.println("版本號更新結果: " + stampUpdated);
System.out.println("新版本號: " + atomicStampedRef.getStamp());
// 正確的方式:使用當前版本號
stampHolder = new int[1];
currentRef = atomicStampedRef.get(stampHolder);
boolean correctUpdate = atomicStampedRef.compareAndSet(
currentRef, "最終數據", stampHolder[0], stampHolder[0] + 1);
System.out.println("使用正確版本號更新結果: " + correctUpdate);
System.out.println("最終引用: " + atomicStampedRef.getReference());
System.out.println("最終版本號: " + atomicStampedRef.getStamp());
}
}
ABA問題詳解:
ABA問題是指:
- 線程1讀取值A
- 線程2將值改為B,然后又改回A
- 線程1進行CAS操作,發現當前值仍是A,于是操作成功
雖然值看起來沒變,但中間狀態的變化可能對業務邏輯產生影響。AtomicStampedReference通過版本號完美解決了這個問題。
五、原子更新字段類
5.1 AtomicIntegerFieldUpdater - 原子更新整型字段
使用場景:優化內存使用、大量對象需要原子字段更新
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
newUpdater(Class<U> tclass, String fieldName) |
tclass: 目標類fieldName: 字段名 |
AtomicIntegerFieldUpdater<U> |
靜態方法創建更新器 |
get(U obj) |
obj: 目標對象 |
int |
獲取字段值 |
set(U obj, int newValue) |
obj: 目標對象newValue: 新值 |
void |
設置字段值 |
getAndSet(U obj, int newValue) |
obj: 目標對象newValue: 新值 |
int |
原子設置并返回舊值 |
compareAndSet(U obj, int expect, int update) |
obj: 目標對象expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndIncrement(U obj) |
obj: 目標對象 |
int |
原子遞增,返回舊值 |
getAndDecrement(U obj) |
obj: 目標對象 |
int |
原子遞減,返回舊值 |
getAndAdd(U obj, int delta) |
obj: 目標對象delta: 增量 |
int |
原子加法,返回舊值 |
incrementAndGet(U obj) |
obj: 目標對象 |
int |
原子遞增,返回新值 |
addAndGet(U obj, int delta) |
obj: 目標對象delta: 增量 |
int |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
* AtomicIntegerFieldUpdater以原子方式更新對象的volatile int字段
* 相比為每個對象創建AtomicInteger,可以節省大量內存
*/
public class AtomicIntegerFieldUpdaterDemo {
static class Counter {
// 必須用volatile修飾,保證可見性
public volatile int count;
private String name;
public Counter(String name, int initialCount) {
this.name = name;
this.count = initialCount;
}
public String getName() { return name; }
public int getCount() { return count; }
}
public static void main(String[] args) {
// 創建字段更新器,指定要更新的類和字段名
AtomicIntegerFieldUpdater<Counter> updater =
AtomicIntegerFieldUpdater.newUpdater(Counter.class, "count");
Counter counter1 = new Counter("計數器1", 0);
Counter counter2 = new Counter("計數器2", 10);
System.out.println("計數器1初始計數: " + counter1.getCount());
System.out.println("計數器2初始計數: " + counter2.getCount());
// get(): 獲取字段值
System.out.println("通過updater獲取計數器1的值: " + updater.get(counter1));
// set(): 設置字段值
updater.set(counter1, 5);
System.out.println("設置計數器1為5后的值: " + counter1.getCount());
// getAndIncrement(): 原子遞增 - 相比synchronized性能更好
int oldCount = updater.getAndIncrement(counter1);
System.out.println("計數器1遞增前值: " + oldCount + ", 當前值: " + counter1.getCount());
// getAndAdd(): 原子加法
oldCount = updater.getAndAdd(counter1, 10);
System.out.println("計數器1加10前值: " + oldCount + ", 當前值: " + counter1.getCount());
// incrementAndGet(): 原子遞增并返回新值
int newCount = updater.incrementAndGet(counter1);
System.out.println("計數器1遞增后的值: " + newCount);
// addAndGet(): 原子加法并返回新值
newCount = updater.addAndGet(counter1, 20);
System.out.println("計數器1加20后的值: " + newCount);
// compareAndSet(): 比較并設置
boolean updated = updater.compareAndSet(counter1, 36, 50);
System.out.println("計數器1 CAS操作結果: " + updated + ", 當前值: " + counter1.getCount());
// 可以同時更新多個對象的相同字段
updater.incrementAndGet(counter2);
System.out.println("計數器2遞增后的值: " + counter2.getCount());
}
}
內存優化效果:
AtomicInteger對象:16-24字節 overheadvolatile int+AtomicIntegerFieldUpdater:4字節 + 靜態updater- 當有大量對象時,內存節省效果顯著
5.2 AtomicLongFieldUpdater - 原子更新長整型字段
使用場景:大數值字段的原子更新、內存敏感場景
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
newUpdater(Class<U> tclass, String fieldName) |
tclass: 目標類fieldName: 字段名 |
AtomicLongFieldUpdater<U> |
靜態方法創建更新器 |
get(U obj) |
obj: 目標對象 |
long |
獲取字段值 |
set(U obj, long newValue) |
obj: 目標對象newValue: 新值 |
void |
設置字段值 |
getAndSet(U obj, long newValue) |
obj: 目標對象newValue: 新值 |
long |
原子設置并返回舊值 |
compareAndSet(U obj, long expect, long update) |
obj: 目標對象expect: 期望值update: 更新值 |
boolean |
CAS操作 |
getAndAdd(U obj, long delta) |
obj: 目標對象delta: 增量 |
long |
原子加法,返回舊值 |
addAndGet(U obj, long delta) |
obj: 目標對象delta: 增量 |
long |
原子加法,返回新值 |
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
/**
* AtomicLongFieldUpdater用于原子更新long字段
* 適用于需要大數值范圍且內存敏感的場景
*/
public class AtomicLongFieldUpdaterDemo {
static class Account {
// 必須用volatile修飾
public volatile long balance;
private final String owner;
public Account(String owner, long initialBalance) {
this.owner = owner;
this.balance = initialBalance;
}
public String getOwner() { return owner; }
public long getBalance() { return balance; }
}
public static void main(String[] args) {
AtomicLongFieldUpdater<Account> balanceUpdater =
AtomicLongFieldUpdater.newUpdater(Account.class, "balance");
Account account1 = new Account("張三", 1000L);
Account account2 = new Account("李四", 2000L);
System.out.println("張三賬戶初始余額: " + account1.getBalance());
System.out.println("李四賬戶初始余額: " + account2.getBalance());
// 基礎操作
System.out.println("通過updater獲取張三余額: " + balanceUpdater.get(account1));
balanceUpdater.set(account1, 1500L);
System.out.println("設置張三余額為1500后的值: " + account1.getBalance());
// 原子存款 - 無鎖線程安全
balanceUpdater.addAndGet(account1, 500L);
System.out.println("張三存款500后余額: " + account1.getBalance());
// 原子取款
long oldBalance = balanceUpdater.getAndAdd(account1, -200L);
System.out.println("張三取款200前余額: " + oldBalance + ", 取款后余額: " + account1.getBalance());
// 比較并設置 - 實現轉賬等業務
boolean transferSuccess = balanceUpdater.compareAndSet(account1, 1800L, 2000L);
System.out.println("張三轉賬操作結果: " + transferSuccess + ", 當前余額: " + account1.getBalance());
// 同時操作多個賬戶
balanceUpdater.getAndAdd(account2, 1000L);
System.out.println("李四存款1000后余額: " + account2.getBalance());
}
}
5.3 AtomicReferenceFieldUpdater - 原子更新引用字段
使用場景:鏈表節點更新、樹結構調整、對象關系維護
核心API詳解
| 方法 | 參數 | 返回值 | 說明 |
|---|---|---|---|
newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) |
tclass: 目標類vclass: 字段類型fieldName: 字段名 |
AtomicReferenceFieldUpdater<U,W> |
靜態方法創建更新器 |
get(U obj) |
obj: 目標對象 |
V |
獲取字段引用 |
set(U obj, V newValue) |
obj: 目標對象newValue: 新引用 |
void |
設置字段引用 |
getAndSet(U obj, V newValue) |
obj: 目標對象newValue: 新引用 |
V |
原子設置并返回舊引用 |
compareAndSet(U obj, V expect, V update) |
obj: 目標對象expect: 期望引用update: 更新引用 |
boolean |
CAS操作 |
lazySet(U obj, V newValue) |
obj: 目標對象newValue: 新引用 |
void |
延遲設置引用 |
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* AtomicReferenceFieldUpdater用于原子更新引用字段
* 常用于實現無鎖數據結構
*/
public class AtomicReferenceFieldUpdaterDemo {
static class Node<T> {
// 必須用volatile修飾
public volatile Node<T> next;
private final T value;
public Node(T value) {
this.value = value;
}
public T getValue() { return value; }
public Node<T> getNext() { return next; }
@Override
public String toString() {
return "Node{value=" + value + ", next=" + (next != null ? next.value : "null") + "}";
}
}
public static void main(String[] args) {
// 創建引用字段更新器
AtomicReferenceFieldUpdater<Node, Node> nextUpdater =
AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");
Node<String> first = new Node<>("第一個節點");
Node<String> second = new Node<>("第二個節點");
Node<String> third = new Node<>("第三個節點");
System.out.println("初始第一個節點的next: " + first.getNext());
// get(): 獲取字段引用
System.out.println("通過updater獲取第一個節點的next: " + nextUpdater.get(first));
// set(): 設置字段引用
nextUpdater.set(first, second);
System.out.println("設置第一個節點的next為第二個節點: " + first);
// compareAndSet(): 原子設置next字段 - 實現無鎖鏈表
boolean setSuccess = nextUpdater.compareAndSet(first, second, third);
System.out.println("CAS操作結果: " + setSuccess);
System.out.println("第一個節點: " + first);
// getAndSet(): 獲取并設置引用
Node<String> oldNext = nextUpdater.getAndSet(second, third);
System.out.println("第二個節點原來的next: " + oldNext);
System.out.println("第二個節點: " + second);
// lazySet(): 延遲設置
nextUpdater.lazySet(third, first); // 形成環狀,僅作演示
System.out.println("第三個節點延遲設置后的next: " + third.getNext());
// 構建鏈表并展示
System.out.println("最終鏈表結構:");
Node<String> current = first;
int count = 0;
while (current != null && count < 5) { // 防止無限循環
System.out.println(current);
current = current.getNext();
count++;
}
}
}
六、綜合實戰:構建線程安全計數器
下面我們通過一個綜合示例展示如何在實際項目中使用原子操作類:
import java.util.concurrent.atomic.*;
import java.util.concurrent.*;
/**
* 線程安全計數器綜合示例
* 展示了多種原子類的實際應用
*/
public class ThreadSafeCounter {
// 基本計數器 - 使用AtomicInteger
private final AtomicInteger count = new AtomicInteger(0);
// 大數值統計 - 使用AtomicLong
private final AtomicLong total = new AtomicLong(0L);
// 狀態控制 - 使用AtomicReference
private final AtomicReference<String> status = new AtomicReference<>("RUNNING");
// 統計數組 - 使用AtomicIntegerArray進行分桶統計
private final AtomicIntegerArray bucketStats = new AtomicIntegerArray(10);
// 配置信息 - 使用AtomicReference支持動態更新
private final AtomicReference<Config> config = new AtomicReference<>(new Config(100, 60));
// 標記位控制 - 使用AtomicBoolean
private final AtomicBoolean enabled = new AtomicBoolean(true);
static class Config {
final int maxConnections;
final int timeoutSeconds;
public Config(int maxConnections, int timeoutSeconds) {
this.maxConnections = maxConnections;
this.timeoutSeconds = timeoutSeconds;
}
@Override
public String toString() {
return "Config{maxConnections=" + maxConnections +
", timeoutSeconds=" + timeoutSeconds + "}";
}
}
// 核心API方法
public void increment() {
if (!enabled.get()) {
System.out.println("計數器已禁用,忽略操作");
return;
}
count.incrementAndGet();
total.addAndGet(1L);
// 分桶統計:根據count值決定放入哪個桶
int bucket = count.get() % 10;
bucketStats.getAndIncrement(bucket);
}
public void add(int value) {
if (!enabled.get()) {
System.out.println("計數器已禁用,忽略操作");
return;
}
count.addAndGet(value);
total.addAndGet(value);
}
public boolean setStatus(String expected, String newStatus) {
return status.compareAndSet(expected, newStatus);
}
public void updateConfig(Config newConfig) {
Config oldConfig;
do {
oldConfig = config.get();
System.out.println("嘗試更新配置: " + oldConfig + " -> " + newConfig);
} while (!config.compareAndSet(oldConfig, newConfig));
System.out.println("配置更新成功");
}
public boolean enable() {
return enabled.compareAndSet(false, true);
}
public boolean disable() {
return enabled.compareAndSet(true, false);
}
// 獲取統計信息
public void printStats() {
System.out.println("\n=== 統計信息 ===");
System.out.println("當前計數: " + count.get());
System.out.println("總數: " + total.get());
System.out.println("狀態: " + status.get());
System.out.println("啟用狀態: " + enabled.get());
System.out.println("桶統計: " + bucketStats.toString());
Config currentConfig = config.get();
System.out.println("配置: " + currentConfig);
// 驗證數據一致性
long sum = 0;
for (int i = 0; i < bucketStats.length(); i++) {
sum += bucketStats.get(i);
}
System.out.println("桶統計總和: " + sum + ", 計數: " + count.get() +
", 一致性: " + (sum == count.get()));
}
public static void main(String[] args) throws InterruptedException {
ThreadSafeCounter counter = new ThreadSafeCounter();
// 創建多個線程同時操作計數器
int threadCount = 10;
int operationsPerThread = 1000;
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
CountDownLatch latch = new CountDownLatch(threadCount);
System.out.println("開始并發測試...");
for (int i = 0; i < threadCount; i++) {
final int threadId = i;
executor.execute(() -> {
try {
for (int j = 0; j < operationsPerThread; j++) {
counter.increment();
// 每隔一定操作數更新配置
if (j % 200 == 0) {
counter.updateConfig(new Config(100 + j, 60));
}
// 模擬隨機禁用/啟用
if (j == 500 && threadId == 0) {
System.out.println("線程" + threadId + "嘗試禁用計數器");
counter.disable();
Thread.sleep(10); // 短暫休眠
System.out.println("線程" + threadId + "嘗試啟用計數器");
counter.enable();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
});
}
// 等待所有線程完成
latch.await();
executor.shutdown();
// 打印最終統計
counter.printStats();
int expectedCount = threadCount * operationsPerThread;
System.out.println("\n=== 測試結果 ===");
System.out.println("期望計數: " + expectedCount);
System.out.println("實際計數: " + counter.count.get());
System.out.println("計數正確: " + (counter.count.get() == expectedCount));
System.out.println("測試" + (counter.count.get() == expectedCount ? "通過" : "失敗"));
}
}
七、原子操作類的工作原理
7.1 CAS機制詳解
CAS(Compare-And-Swap)是原子操作類的核心,包含三個操作數:
- 內存位置(V)
- 期望原值(A)
- 新值(B)
CAS的語義是:"我認為V的值應該是A,如果是,那么將V的值更新為B,否則不修改并告訴我現在的值是多少"
CAS操作是硬件級別的原子操作,在現代CPU中通常通過以下方式實現:
- x86架構:
CMPXCHG指令 - ARM架構:
LDREX/STREX指令對
7.2 Unsafe類的作用
所有原子操作類底層都依賴sun.misc.Unsafe類,它提供了硬件級別的原子操作:
public final class Unsafe {
// 對象字段操作
public native long objectFieldOffset(Field f);
// 數組基礎偏移
public native int arrayBaseOffset(Class arrayClass);
// 數組索引縮放
public native int arrayIndexScale(Class arrayClass);
// CAS操作
public final native boolean compareAndSwapObject(Object o, long offset,
Object expected, Object x);
public final native boolean compareAndSwapInt(Object o, long offset,
int expected, int x);
public final native boolean compareAndSwapLong(Object o, long offset,
long expected, long x);
// 獲取和設置值
public native int getIntVolatile(Object o, long offset);
public native void putIntVolatile(Object o, long offset, int x);
// 延遲設置(有更弱的可見性保證)
public native void putOrderedInt(Object o, long offset, int x);
}
7.3 內存屏障與可見性
原子操作類通過內存屏障保證可見性:
- 寫操作:在寫入后插入寫屏障,保證寫入對其他線程可見
- 讀操作:在讀取前插入讀屏障,保證讀取到最新值
Java內存模型中的屏障類型:
- LoadLoad屏障:保證該屏障前的讀操作先于屏障后的讀操作完成
- StoreStore屏障:保證該屏障前的寫操作先于屏障后的寫操作完成
- LoadStore屏障:保證該屏障前的讀操作先于屏障后的寫操作完成
- StoreLoad屏障:保證該屏障前的所有寫操作對其他處理器可見
八、性能對比與選型建議
8.1 性能對比
| 場景 | synchronized | 原子操作類 | 性能提升 |
|---|---|---|---|
| 低競爭 | 慢 | 快 | 2-10倍 |
| 中等競爭 | 中等 | 中等 | 相當 |
| 高競爭 | 快 | 慢(自旋) | 可能更差 |
8.2 不同原子類的性能特點
| 原子類 | 適用場景 | 性能特點 |
|---|---|---|
AtomicInteger |
普通計數器 | 性能優秀,適用大部分場景 |
AtomicLong |
大數值計數 | 在32位系統上性能較差 |
LongAdder |
高并發統計 | 高競爭環境下性能最優 |
AtomicReference |
對象引用更新 | 性能與對象大小相關 |
| 字段更新器 | 內存敏感場景 | 節省內存,性能稍差 |
8.3 選型指南
-
計數器場景
- 簡單計數:
AtomicInteger - 大數值計數:
AtomicLong或LongAdder - 分桶統計:
AtomicIntegerArray
- 簡單計數:
-
狀態控制
- 布爾標志:
AtomicBoolean - 對象狀態:
AtomicReference - 帶版本狀態:
AtomicStampedReference
- 布爾標志:
-
內存敏感場景
- 大量對象:字段更新器(
AtomicXXXFieldUpdater) - 緩存系統:
AtomicReference
- 大量對象:字段更新器(
-
數據結構
- 無鎖隊列:
AtomicReference - 無鎖棧:
AtomicReference - 無鎖鏈表:
AtomicReferenceFieldUpdater
- 無鎖隊列:
8.4 最佳實踐
- 避免過度使用:不是所有場景都需要原子類
- 注意ABA問題:必要時使用帶版本號的原子類
- 考慮高競爭:高競爭環境下考慮
LongAdder等替代方案 - 內存布局:字段更新器可以優化內存使用
- JDK8+特性:利用新的函數式更新方法
- 性能測試:在實際環境中進行性能測試
九、總結
Java原子操作類為我們提供了強大的無鎖并發編程工具:
9.1 核心價值
- 13個原子類覆蓋了基本類型、數組、引用和字段更新
- CAS機制基于硬件指令,性能優異
- 無鎖設計避免了死鎖和鎖開銷
- 豐富的API支持各種并發場景
9.2 使用場景總結
| 類別 | 主要類 | 核心用途 |
|---|---|---|
| 基本類型 | AtomicInteger, AtomicLong, AtomicBoolean |
計數器、狀態標志 |
| 數組 | AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray |
并發數組、分桶統計 |
| 引用 | AtomicReference, AtomicStampedReference, AtomicMarkableReference |
對象緩存、狀態管理 |
| 字段更新 | AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater |
內存優化、大量對象 |
9.3 學習建議
- 從簡單開始:先掌握
AtomicInteger和AtomicReference - 理解原理:深入理解CAS機制和內存模型
- 實踐應用:在真實項目中嘗試使用原子類
- 性能調優:根據實際場景選擇合適的原子類
- 持續學習:關注JDK新版本中的并發工具改進
掌握這些原子操作類,能夠讓我們在適當的場景下寫出更高效、更安全的并發代碼。記住,工具雖好,但要因地制宜,根據具體場景選擇最合適的并發控制方案。
希望本文能幫助你深入理解Java原子操作類,在實際項目中游刃有余地處理并發問題!
進一步學習資源:
?? 如果你喜歡這篇文章,請點贊支持! ?? 同時歡迎關注我的博客,獲取更多精彩內容!
本文來自博客園,作者:佛祖讓我來巡山,轉載請注明原文鏈接:http://www.rzrgm.cn/sun-10387834/p/19172186

浙公網安備 33010602011771號