Java線程安全之CAS原子操作

首先介紹一下線程安全的概念:

1.競態條件與臨界區
多個線程訪問了相同的資源,向這些資源做了寫操作時,對執行順序有要求。
臨界區:方法內部某些部分區域代碼,如果多線程執行時,會對結果產生不可預測影響。
競態條件:當臨界區代碼被多線程執行時,產生競態條件。

2.共享資源
如果一段代碼是線程安全的,則它不包含競態條件。只有當多個線程更新共享資源時,纔會產生競態條件
棧封閉時,不會在線程之間共享變量,因此是線程安全的
局部對象引用本身不共享,但是引用的對象存儲在共享堆中。如果在方法內創建對象,只是在線程內部傳遞,並且不對其他線程可用,那麼也是線程安全的

3.不可變對象
實例被創建後,對象相關變量就不能再被修改,這就是不可變性。創建不可變的共享對象來保證對象在線程之間共享時不會被修改,從而實現線程安全。

原子操作

爲了解決多線程同時寫的線程安全,需要實現原子操作
原子操作可以是一個步驟,也可以是多個步驟,但是其順序不可以被打亂,也不可以被切割而只執行其中的一部分(不可中斷性)。將整個操作是爲一個整體,資源在該次操作中保持一致,這就是原子性的核心特徵。存在競態條件時,線程不安全,需要轉變爲原子操作才能實現線程安全,方式:CAS,鎖。本篇主要介紹CAS操作。

CAS機制

CAS(Compare and swap)比較和交換,屬於硬件同步原語,處理器提供了基本內存操作的原子性保證。CAS操作需要輸入兩個數值,一箇舊值A(期望操作前的值)和一個新的值B,在操作期間先比較下舊值有沒有發生變化,如果沒有發生變化,纔會換成新值,發生了變化則不會換成新值。

Java中的sun.misc.Unsafe類,提供了compareAndSwapXxx()等幾個native方法實現CAS,J.U.C併發包中大量用到了這些方法。
Unsafe對象不能直接使用Unsafe.getUnsafe()獲取,具體使用方式如下:

Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = theUnsafe.get(null);
long valueOffset = unsafe.objectFieldOffset(ClassName.class.getDeclaredField("fieldName"));
int current = unsafe.getIntVolatile(object, valueOffset);
boolean isSuccess = unsafe.compareAndSwapInt(object, valueOffset, current, current+addValue);

CAS的三個問題:
循環+CAS,自旋的實現讓所有線程都處於高頻運行、爭搶CPU執行時間的狀態。如果操作長時間不成功,會帶來很大的CPU資源消耗
僅能針對單個變量的原子操作,不能用於多個變量來實現原子操作
ABA問題,無法體現出數據的變動

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