理解ABA問題

2021-10-23 17:24:14 字數 4393 閱讀 2277

所謂aba問題,就是比較並交換的迴圈,存在乙個時間差,而這個時間差可能帶來意想不到的問題。比如執行緒1和執行緒2同時也從記憶體取出a,執行緒t1將值從a改為b,然後又從b改為a。執行緒t2看到的最終值還是a,經過與預估值的比較,二者相等,可以更新,此時儘管執行緒t2的cas操作成功,但不代表就沒有問題。

有的需求,比如cas,只注重頭和尾的一致,只要首尾一致就接受。但是有的需求,還看重過程,中間不能發生任何修改,這就引出了atomicreference原子引用

atomicinteger對整數進行原子操作,atomicinteger對長整型數進行原子操作,atomicboolean對布林型數進行原子操作,但實際上這些是完全不夠的,如果是乙個pojo呢?可以用atomicreference來包裝這個pojo,使其操作原子化

class atomicreference < v >,value就是我們需要進行原子包裝的泛型類。

示例:

@getter

@tostring

@allargsconstructor

class

user

public

class

atomicrefrencedemo

}

輸出結果:

true user(username=李四, age=23)

false user(username=李四, age=23)

那麼我們如何在原子引用的基礎上,解決aba問題呢,請看帶時間戳的原子引用 atomicstampedreference。

使用atomicstampedreference類可以解決aba問題。這個類維護了乙個「版本號」stamp「,在進行cas操作的時候,不僅要比較當前值,還要比較版本號。只有兩者都相等,才執行更新操作。

核心方法:

static atomicstampedreference

atomicstampedreference =

newatomicstampedreference

<

>

(initialref, initialstamp)

;int stamp = atomicstampedreference.

getstamp

()

atomicstampedreference.

compareandset

(expectedreference,newreference,oldstamp,newstamp)

;

示例:

public

class

abademo

,"thread 1").

start()

;new

thread((

)->

catch

(interruptedexception e)

system.out.

println

(atomicreference.

compareandset

(100

,2019)+

"\t"

+ atomicreference.

get())

;},"thread 2").

start()

;try

catch

(interruptedexception e)

system.out.

println

("*****以下時aba問題的解決*****");

newthread((

)->

catch

(interruptedexception e)

atomicstampedreference.

compareandset

(100

,101

, atomicstampedreference.

getstamp()

, atomicstampedreference.

getstamp()

+1);

system.out.

println

(thread.

currentthread()

.getname()

+"\t第2次版本號"

+ atomicstampedreference.

getstamp()

);atomicstampedreference.

compareandset

(101

,100

, atomicstampedreference.

getstamp()

, atomicstampedreference.

getstamp()

+1);

system.out.

println

(thread.

currentthread()

.getname()

+"\t第3次版本號"

+ atomicstampedreference.

getstamp()

);},

"thread 3").

start()

;new

thread((

)->

catch

(interruptedexception e)

boolean result = atomicstampedreference.

compareandset

(100

,2019

, stamp, stamp +1)

; system.out.

println

(thread.

currentthread()

.getname()

+"\t修改是否成功"

+ result +

"\t當前最新實際版本號:"

+ atomicstampedreference.

getstamp()

);system.out.

println

(thread.

currentthread()

.getname()

+"\t當前最新實際值:"

+ atomicstampedreference.

getreference()

);},

"thread 4").

start()

;}}

輸出結果:

***** 以下時aba問題的產生 *****

true 2019

***** 以下時aba問題的解決 *****

thread 3 第1次版本號1//初始版本號

thread 4 第1次版本號1//初始版本號

thread 3 第2次版本號2//第一次修改後的版本號

thread 3 第3次版本號3//第二次修改後的版本號

thread 4 修改是否成功false 當前最新實際版本號:3//修改失敗,此時t4的版本號為1+1,但實際t3已經將版本號增加到了3,t4修改失敗

thread 4 當前最新實際值:100

try

catch

(interruptedexception e)

t3執行緒拿到第一次版本號後睡眠2秒,保證t4執行緒能拿到和它一樣的初始版本號。

try

catch

(interruptedexception e)

t4執行緒拿到第一次版本號後再睡眠4秒,保證在此期間t3執行緒已經完成了一次aba操作。

atomicstampedreference.

compareandset

(100

,101

, atomicstampedreference.

getstamp()

, atomicstampedreference.

getstamp()

+1);

第乙個引數代表預估值,第二個引數代表更新值,第三個引數代表預估版本號,第四個引數代表更新版本號。

如果預估值與記憶體實際值相等,預估版本號與實際版本號相等,則更新記憶體值為更新值,更新版本號為更新版本號。

簡介ABA問題

在多執行緒計算中,在同步期間會發生aba問題,當乙個位置被讀取兩次,兩次讀取具有相同的值,並且 值相同 用於指示 什麼都沒有改變 時。但是,另乙個執行緒可以在兩次讀取之間執行並更改值,執行其他工作,然後將值改回,因此,即使第二個執行緒的工作違反了該假設,也使第乙個執行緒認為 什麼都沒有改變 當多個執...

ABA問題及解決

aba問hiyi題 在多執行緒環境下,乙個執行緒需要修改共享變數的值,使用cas操作時,當其他執行緒將該共享變數由a該為b,再將b改為a後,這個執行緒依然可以cas操作成功,因為這個執行緒不能感知這個共享變數被修改過 解決方法 給共享變數增加乙個版本號,在cas操作時不僅比較值是否相等,還比較版本號...

CAS的ABA問題詳解

aba問題 執行緒1搶先獲得cpu時間片,而執行緒2因為其他原因阻塞了,執行緒1取值與期望的a值比較,發現相等然後將值更新為b,然後這個時候出現了執行緒3,期望值為b,欲更新的值為a,執行緒3取值與期望的值b比較,發現相等則將值更新為a,此時執行緒2從阻塞中恢復,並且獲得了cpu時間片,這時候執行緒...