我們現在來說什么是ABA問題。假設內存中有一個值為A的變量,存儲在地址V中。
此時有三個線程想使用CAS的方式更新這個變量的值,每個線程的執行時間有略微偏差。線程1和線程2已經獲取當前值,線程3還未獲取當前值。
接下來,線程1先一步執行成功,把當前值成功從A更新為B;同時線程2因為某種原因???被阻塞住,沒有做更新操作;線程3在線程1更新之后,獲取了當前值B。
為什么線程3又反過來操作???是自旋嗎???
在之后,線程2仍然處於阻塞狀態,線程3繼續執行,成功把當前值從B更新成了A。
最后,線程2終於恢復了運行狀態,由於阻塞之前已經獲得了“當前值A”,並且經過compare檢測,內存地址V中的實際值也是A,所以成功把變量值A更新成了B。
個人見解: 不是說一定出現ABA情況,是說有出現這種情況的可能性。也就是舉一個特例來說明問題的可能性???
看起來這個例子沒啥問題,但如果結合實際,就可以發現它的問題所在。
我們假設一個提款機的例子。假設有一個遵循CAS原理的提款機,小灰有100元存款,要用這個提款機來提款50元 。
由於提款機硬件出了點問題,小灰的提款操作被同時提交了兩次,開啟了兩個線程,兩個線程都是獲取當前值100元,要更新成50元。
理想情況下,應該一個線程更新成功,一個線程更新失敗,小灰的存款值被扣一次。
有個問題: 點兩次正常情況下不是兩次都成功嗎?為什么一次成功一次失敗???
線程1首先執行成功,把余額從100改成50.線程2因為某種原因 什么原因???阻塞。這時,小灰的媽媽剛好給小灰匯款50元。
線程2仍然是阻塞狀態,線程3執行成功,把余額從50改成了100。
線程2恢復運行,由於阻塞之前獲得了“當前值”100,並且經過compare檢測,此時存款實際值也是100,所以會成功把變量值100更新成50。
原本線程2應當提交失敗,小灰的正確余額應該保持100元,結果由於ABA問題提交成功了。
怎么解決呢?加個版本號就可以了。
真正要做到嚴謹的CAS機制,我們在compare階段不僅要比較期望值A和地址V中的實際值,還要比較變量的版本號是否一致。
我們仍然以剛才的例子來說明,假設地址V中存儲着變量值A,當前版本號是01。線程1獲取了當前值A和版本號01,想要更新為B,但是被阻塞了。
這時候,內存地址V中變量發生了多次改變,版本號提升為03,但是變量值仍然是A。
隨后線程1恢復運行,進行compare操作。經過比較,線程1所獲得的值和地址的實際值都是A,但是版本號不相等,所以這一次更新失敗。
在Java中,AtomicStampedReference類???就實現了用版本號作比較額CAS機制。
1. java語言CAS底層如何實現?
利用unsafe提供的原子性操作方法(我也不太清楚???)。
2.什么事ABA問題?怎么解決?
當一個值從A變成B,又更新回A,普通CAS機制會誤判通過檢測。
利用版本號比較可以有效解決ABA問題。
參考文檔:https://blog.csdn.net/qq_32998153/article/details/79529704