【Java並發編程實戰】-----“J.U.C”:CAS操作


CAS,即Compare and Swap,中文翻譯為“比較並交換”。

對於JUC包中,CAS理論是實現整個java並發包的基石。從整體來看,concurrent包的實現示意圖如下:

201511040000·

i++是一個非常經典的操作,它幾乎充斥着我們每個人編寫的代碼中。我們知道i++是可以分解的,它分解為getI()、i + 1 、setI三個步驟,所以它並不是原子操作。如果i==1,執行兩次i++操作,我們期望的結果是3,但是結果有可能也是2:

2015110500001

那么有什么辦法解決這個問題呢?肯定有!使用鎖即可:

synchronized(this){
            i++;
        }

誠然,在java中存在樂觀鎖、悲觀鎖兩種鎖。其中synchronized就是悲觀鎖,在前面我們了解synchronized也是獨占鎖,加入關鍵字synchronized的代碼一般都是以單線程的形式在運行着,它會導致其他需要該資源的線程掛起直到前面的線程執行完畢釋放資源,所以它的效率較為低下。而樂觀鎖則采用了一種較為高效的方式,它的操作與synchronized不同,synchronized采用加鎖,而它則不采用加鎖去執行某些操作,如果發生了沖突則失敗並一直重試直到成功為止。而CAS就是一種樂觀鎖,它所采用的策略是當且僅當預期值A和存中的值V相同,則將內存V值修改為B,否則返回V。實現如下:

for(;;){
            if(A==V){
                V = B;
            }
        }

當然在J.U.C中實現CAS沒有這么簡單。

CAS

CAS,即一種對內存中的共享數據進行操作的指令,而且該操作是原子的讀寫操作。其過程如下:首先CPU將內存中的將要被修改的數據與預期的值進行比較,如果這兩個值相等,CPU則會將內存中數值替換為新值,否則不做操作。最后,CPU會將舊值返回。在java中,CAS的含義就是“我認為的原本的值是什么,如果你是,則更換為新值,否則不做修改同時麻煩告訴我該值時多少”

在CAS中,總共存在三個操作數:預期值A、內存中的V、修改的值B。當且僅當預期值A和內存中的值V相同,則將內存V值修改為B,否則返回V。使用這種機制編寫的算法也叫作非阻塞算法,標准定義了一個線程的失敗或者掛起是不會影響其他線程的失敗或者掛起。

下面我們來已AtomicIneger的源碼為例來看看CAS操作:

public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

這里很顯然使用CAS操作(for(;;)里面),他每次都從內存中讀取數據,+1操作,然后兩個值進行CAS操作。如果成功則返回,否則失敗重試,直到修改成功為止。

上面源碼最關鍵的地方有兩個,一個for循環,它代表着一種寧死不屈的精神,不成功誓不罷休。還有就是compareAndSet:

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

CAS的缺陷

盡管CAS機制可以使我們不依賴與同步,不影響和掛起其他線程,它大大提升了運行的效率,但是它會導致一個ABA的問題,如下:加入有兩個線程A、B,他們都讀取內存中的數據V,假如這個時候線程A,先將V修改為V1,然后又修改為V,這個時候線程B的compareAndSet仍然能成功,對於線程B而言該值V並沒有發生任何變化,而實際上它已經變化了,只不過最后又還原了而已。

 

參考文獻

1、Java的多線程編程模型5--Java中的CAS理論

2、JAVA CAS原理深度分析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM