目錄
一. Atomic基本類型
1.基本數據類型
- AtomicInteger
- AtomicLong
- AtomicBoolean
2.常用方法
- int get() // 獲取當前值
- int getAndSet(int value) // 獲取當前值並設置成指定值等價於普通變量的i = value
- int getAndIncrement() // 自增 ,等價於普通變量的i++
- int getAndDecrement() // 自減,等價於普通變量的i–
- int getAndAdd(int Value) // 加上預期值,等價於普通變量的i = i + value
- boolean compareAndSet(int expect, int update) //如果該對象等於期望值,把它更新成update的值,等價於普通變量的`if (i == expect) {i = update}
二. AtomicInteger
- AtomicInteger 底層採用的是CAS原理(CompareAndSwap)。AtomicInteger 類型的對象,之所以在對AtomicInteger對象操作時,能保證線程安全,是因爲在進行操作時,會將當前對象的值與內存中變量的值進行比較,如果是一致的,則進行變量操作,否則繼續去內存中獲取變量的值,直至當前值,與內存中的值是一致的。
- AtomicInteger 對象,可以通過調用incrementAndGet()方法和getAndIncrement()方法進行值加1的操作,區別在於:incrementAndGet()方法先執行加1操作,然後獲取值;getAndIncrement()方法是先獲取值然後執行加1的操作;
三. AtomicInteger 對象使用示例:
1.非線程安全的使用示例:
@Slf4j
public class CountExample1 {
// 請求總數
public static int clientTotal = 5000;
// 同時併發執行的線程數
public static int threadTotal = 200;
public static int count = 0;
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count);
}
private static void add() {
count++;
}
}
上述代碼每次執行過程中,輸出的count
值都有可能會不相同,而且小於5000,這是因爲count++
操作時,會發生併發問題;
2. 使用AtomicInteger 實現線程安全的使用示例:
@Slf4j
public class AtomicExample1 {
// 請求總數
public static int clientTotal = 5000;
// 同時併發執行的線程數
public static int threadTotal = 200;
//創建AtomicInteger對象並初始化
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();
final Semaphore semaphore = new Semaphore(threadTotal);
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal ; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();
add();
semaphore.release();
} catch (Exception e) {
log.error("exception", e);
}
countDownLatch.countDown();
});
}
countDownLatch.await();
executorService.shutdown();
log.info("count:{}", count.get());
}
private static void add() {
count.incrementAndGet(); //先增加後獲取
// count.getAndIncrement(); //先獲取後增加
}
}
3. AtomicInteger線程安全底層實現
上述AtomicInteger對象執行incrementAndGet()方法時,底層方法是:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
其中getAndAddInt()方法的源碼如下:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
其中compareAndSwapInt()
方法是CAS機制中的一種,它的源碼如下:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
由於compareAndSwapInt()
方法是被native
關鍵字修飾,說明這個方法是原生函數,也就是這個方法是用C/C++語言實現的,並且被編譯成了DLL,由java去調用。這些函數的實現體在DLL中,JDK的源代碼中並不包含,我們是看不到的。
getAndAddInt()
方法執行過程如下這篇文章所示:getAndAddInt()執行過程
二、原子類的ABA問題
原因:
- 所謂ABA就是一個線程在CAS更新變量的時候,該變量已經被別的線程先從A該成了B,再從B該回了A,此時線程還是會CAS成功,因爲它認爲期望值並沒有變
解決辦法:
- 解決辦法是增加版本號,每次修改版本號加1
- 具體解決方法:使用版本號功能的類爲
AtomicStampedReference
Java併發編程學習系列
如有幫助,煩請點贊收藏一下啦 (◕ᴗ◕✿)