java並發之CAS詳解

來源:互聯網
上載者:User

標籤:shm   地址   多線程   進入   volatile   ctf   語言   更新   cal   

前言

在高並發的應用當中,最關鍵的問題就是對共用變數的安全訪問,通常我們都是通過加鎖的方式,比如說synchronized、Lock來保證原子性,或者在某些應用當中,用voliate來保證變數的可見度,還有就是通過TheadLocal將變數copy一份,稱為局部變數(線程私人)等等。現在我們學習一種不加鎖機制(CAS)

上述我們提到的synchronized和Lock這都是通過加鎖實現的(悲觀鎖),其實加鎖本質上是將並發轉變成串列實現的,勢必會阻塞線程的執行,影響應用的輸送量,而CAS正是一種樂觀的策略,並不會出現加鎖來阻塞線程的執行。

CAS簡介

CAS,Compare And Swap,即比較並交換。Atomic原子類操作等等都是以CAS實現的,還有ConcurrentHashMap在JDK1.8的版本也調整為了CAS+Synchronized

分析(自旋中對應下文的native方法)

在CAS中有三個參數:記憶體值V、舊的預期值A、要更新的值B。若且唯若記憶體值V的值等於舊的預期值A時才會將記憶體值V的值修改為B,否則什麼都不幹

虛擬碼如下:

if(this.value == A){    this.value = B    return true;}else{    return false;}
應用

在java.util.concurrent.atomic包下原子類都是通過CAS來實現的,現在我們以AtomicInteger為例來分析一下CAS的實現

    private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {    try {        valueOffset = unsafe.objectFieldOffset            (AtomicInteger.class.getDeclaredField("value"));    } catch (Exception ex) { throw new Error(ex); }}private volatile int value;

Unsafe是CAS的核心類,Java無法直接存取底層作業系統,而是通過本地(native)方法來訪問。不過儘管如此,JVM還是開了一個後門:Unsafe,它提供了硬體層級的原子操作。

valueOffset為變數值在記憶體中的位移地址,unsafe就是通過位移地址來得到資料的原值的。

value當前值,使用volatile修飾,保證多線程環境下看見的是同一個。

下面我們以AtomicInteger的addAndGet()方法來說明

    public final int getAndAdd(int delta) {    return unsafe.getAndAddInt(this, valueOffset, delta);}

內部調用的是Unsafe類的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;}

而在getAndAddInt方法中又調用了Unsafe的本地方法

public native int getIntVolatile(Object var1, long var2);    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);    

此方法是本地方法(java需要調用其他語言的代碼,比如C語言代碼,跟dll檔案有關),有四個參數,分別代表:對象、對象的地址、預期值、修改值

CAS的缺陷
  • 樂觀鎖只能保證一個共用變數的原子操作。如上例子,自旋過程中只能保證value變數的原子性,這時如果多一個或幾個變數,樂觀鎖將變得力不從心,但互斥鎖能輕易解決,不管對象數量多少及對象顆粒度大小。

  • 長時間自旋可能導致開銷大。假如CAS長時間不成功而一直自旋,會給CPU帶來很大的開銷。

  • ABA問題。CAS的核心思想是通過比對記憶體值與預期值是否一樣而判斷記憶體值是否被改過,但這個判斷邏輯不嚴謹,假如記憶體值原來是A,後來被一條線程改為B,最後又被改成了A,則CAS認為此記憶體值並沒有發生改變,但實際上是有被其他線程改過的,這種情況對依賴過程值的情景的運算結果影響很大。解決的思路是引入版本號碼,每次變數更新都把版本號碼加一。

synchronized與CAS的區別
  • 對於資源競爭較少的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及使用者態核心態間的切換操作額外浪費消耗cpu資源;而CAS基於硬體實現,不需要進入核心,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的效能。

  • 對於資源競爭嚴重的情況,CAS自旋的機率會比較大(比如getAndAddInt方法中的do-while迴圈),從而浪費更多的CPU資源,效率低於synchronized。

java並發之CAS詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.