所謂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)那麼我們如何在原子引用的基礎上,解決aba問題呢,請看帶時間戳的原子引用 atomicstampedreference。false user(username=李四, age=23)
使用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時間片,這時候執行緒...