CAS實現原子操作的三個問題

在java中,如果要進行原子操作,我們可以通過加鎖或CAS的方式來實現。其中,CAS雖然高效的解決了原子操作,但需要注意其存在的三個問題:ABA問題、自旋時間長開銷大、只能保證一個共享變量的原子操作。

一、ABA問題

CAS 在修改變量值時,會先檢查該變量的值是否和預期值一致,若一致則修改,引發的ABA問題的情況是:如一個變量初始值爲A,被另外一個線程修改成B,再由B修改爲A,此時使用CAS進行操作就檢查不出變量的變化軌跡,並對該變量修改,這就是ABA問題,其前提條件是“節點可以被循環使用”。

ABA問題的解決辦法是在變量前加上版本號,每次變量的修改都將版本號加1,此時 A → B → A 則變成 1A → 2B → 3A。從而避免ABA問題。

JDK 1.5 之後再併發包下也提供了對應的帶郵戳的原子類 AtomicStampedReference以解決ABA問題。

/**
 * 原子設置 reference 和 stamp 爲給定的更新值,
 * 當且當前的引用== 期望的引用,stamp和期望的stamp相等
 *
 * @param expectedReference the expected value of the reference
 * @param newReference the new value for the reference
 * @param expectedStamp the expected value of the stamp
 * @param newStamp the new value for the stamp
 * @return {@code true} if successful
 */
public boolean compareAndSet(
							 V   expectedReference, // 期望的引用
                             V   newReference, // 新的引用
                             int expectedStamp,// 期望的stamp
                             int newStamp // 新的stamp
                             ) {
    Pair<V> current = pair;
    return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}

二、自旋時間長開銷大

如果CAS自旋長時間仍不成功,對CPU的開銷是非常大的。

三、只能保證一個共享變量的原子操作

CAS可以保證單個共享變量的原子操作,對於多個共享變量,CAS無法保證原子性,當然此時可以使用鎖來解決。另外一個方法是:把多個共享變量封裝進一個對象,然後通過AtomicReference類保證引用對象之間的原子性。



參考:
1.《Java併發編程的藝術》-Java併發機制的底層實現原理(第2章)
2.《JAVA併發編程實戰》,Doug lea

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章