文章目錄
- 前言
- AtomicInteger的簡單使用
- Atomic介紹
- CAS的實作原理
- 一定會有人疑問,既然++的操作都沒有辦法保證原子性操作,那么CAS又如何能保證是一個原子性操作?
- Atomic存在的三個問題
前言
前面兩個章節,通過解釋執行緒同步,引入了執行緒鎖synchronized隱式鎖,讓大家對鎖有了深刻的認識,
執行緒安全(一)synchronized 什么是執行緒同步?什么是執行緒安全?什么是執行緒鎖?synchronized怎么用?如何理解wait()和sleep()的區別?超詳細例程講解-------手摸手教會小白
在顯式鎖lock的章節中,對synchronized和lock介紹了一些對比,通過這樣的方式介紹了lock的相關內容,同事介紹了鎖的一些其他概念
執行緒安全(二)Lock 什么是Lock執行緒鎖?與synchronized區別在哪?Lock鎖是如何實作等待通知的?如何實作執行緒順序執行?
那么,這個章節將要介紹一下Atomic樂觀鎖,
AtomicInteger的簡單使用
首先,通過一個很簡單的實體,感受一下AtomicInteger與int 的區別,
public class AtomicRunable implements Runnable {
private static int count;
private static AtomicInteger atomicInteger = new AtomicInteger(0);
@Override
public void run() {
for (int i=0;i<10000;i++){
count++;
atomicInteger.incrementAndGet();
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[1000];
Runnable runnable = new AtomicRunable();
for (int i = 0; i < 1000; i++) {
threads[i] = new Thread(runnable) ;
threads[i].start();
}
Thread.sleep(3000);
System.out.println(count);
System.out.println(atomicInteger);
}
}
這個例程是1000個執行緒,每個執行緒都對同一個引數做10000次++的操作,
8788841
10000000
從結果上可以看出,使用AtomicInteger之后,無論執行多少次,每次都能夠保證原子性,因此Atomic實作了和lock、synchronized同樣的目的,
Atomic介紹
在JDK1.5之后,JDK的(concurrent包)并發包里提供了一些類來支持原子操作,如AtomicBoolean,AtomicInteger,AtomicLong等都是用原子的方式來更新指定型別的值,
從多執行緒并行計算樂觀鎖 和 悲觀鎖 來講,JAVA中的synchronized 屬于悲觀鎖,即是在操作某資料的時候總是會認為多執行緒之間會相互干擾,屬于阻塞式的加鎖;Atomic系列則屬于樂觀鎖系列,即當操作某一段資料的時候,執行緒之間是不會相互影響,采用非阻塞的模式,直到更新資料的時候才會進行版本的判斷是否值已經進行了修改,即CAS操作,
CAS的實作原理
假設右邊這是很多執行緒,都想將左邊的這個值做+1的操作,右邊的這些執行緒都可以看到左邊這個引數的數值,所以這些執行緒會把看到的值拿來做計算,計算出運算之后值應該是多少,以執行緒1為例,看到的值為0,要修改為1,其他執行緒同樣如此,
這些執行緒都要做CAS操作,即compare and change,比較和交換,將0拿去比較,如果左邊的引數還是0的話,就換成1,這么多執行緒,現在都是拿著0去比較,且都要換成1.顯然這時只有一個執行緒能成功,

假設執行緒1成功交換,左邊的引數被換成1了,此時,其他執行緒過來做CAS操作時發現,引數已經不再是0了,所以其他執行緒需要做自旋操作,也就是把新看到的值拿來做計算,然后再次進行cas操作,

其他執行緒自選后如下圖所示,所有執行緒都以這樣的方式執行,如果不成功就一直自旋直到成功為止,

一定會有人疑問,既然++的操作都沒有辦法保證原子性操作,那么CAS又如何能保證是一個原子性操作?
所有的執行緒都是cpu執行的,所以如果在cpu的層面能夠確保原子性,那么就可以認為這是個原子性操作,比如cpu提供一個指令就叫做compare and swap 常見的x86 有個指令cmpxchg 支持cas,也就是計算機的硬體支持了這個原子性操作 還有ARM 架構 LL/SC 指令 支持CAS,
所以是CPU層面架構支持,就不需要在作業系統的層面去 那java在Atmoic類中有一個Unsafe屬性,unsafe這個類里面有個compareAndSwap方法是個原生的方法在openjdk原始碼中嵌了匯編代碼 cmpxchg , 所以是在匯編層有這樣的硬體指令支持,因此它是原子性的,
Atomic存在的三個問題
| 序號 | 問題 |
|---|---|
| 1 | ABA問題,CAS在操作值的時候檢查值是否已經變化,沒有變化的情況下才會進行更新,但是如果一個值原來是A,變成B,又變成A,那么CAS進行檢查時會認為這個值沒有變化,但是實際上卻變化了,ABA問題的解決方法是使用版本號,在變數前面追加上版本號,每次變數更新的時候把版本號加一,那么A-B-A 就變成1A-2B-3A,從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題, |
| 2 | 并發越高,失敗的次數會越多,CAS如果長時間不成功,會極大的增加CPU的開銷,因此CAS不適合競爭十分頻繁的場景, |
| 3 | 只能保證一個共享變數的原子操作,當對多個共享變數操作時,CAS就無法保證操作的原子性,這時就可以用鎖,或者把多個共享變數合并成一個共享變數來操作,比如有兩個共享變數i=2,j=a,合并一下ij=2a,然后用CAS來操作ij,從Java1.5開始JDK提供了AtomicReference類來保證參考物件的原子性,你可以把多個變數放在一個物件里來進行CAS操作, |
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/255566.html
標籤:java
