CAS原理分析,解決銀行轉(zhuǎn)賬ABA難題
掃描二維碼
隨時(shí)隨地手機(jī)看文章
什么是CAS
Unsafe源碼分析
Unsafe有很多個(gè)CAS操作的相關(guān)方法,這里舉例幾個(gè)
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
-
將對(duì)象引用、值在對(duì)象中的偏移量、期望的值和欲更新的新值傳遞給Unsafe.cpp
-
如果值更新成功則返回true給開發(fā)者,沒有更新則返回false
-
接受從Unsafe傳遞過來的對(duì)象引用、偏移量、期望的值和欲更新的新值,根據(jù)對(duì)象引用和偏移量計(jì)算出值的地址,然后將值的地址、期望的值、欲更新的新值傳遞給CPU
-
如果值更新成功則返回true給Unsafe.java,沒有更新則返回false
-
接受從Unsafe.cpp傳遞過來的地址、期望的值和欲更新的新值,執(zhí)行指令cmpxchg,比較地址中的值是否和期望的值一樣,一樣則將值更新為新的值,不一樣則不做任何操作
-
將操作結(jié)果返回給Unsafe.cpp
CAS的缺點(diǎn)
ABA問題
-
線程1,期望值為A,欲更新的值為B
-
線程2,期望值為A,欲更新的值為B
小明在提款機(jī),提取了50元,因?yàn)樘峥顧C(jī)問題,有兩個(gè)線程,同時(shí)把余額從100變?yōu)?0
線程1(提款機(jī)):獲取當(dāng)前值100,期望更新為50,
線程2(提款機(jī)):獲取當(dāng)前值100,期望更新為50,
線程1成功執(zhí)行,線程2某種原因block了,這時(shí),某人給小明匯款50
線程3(默認(rèn)):獲取當(dāng)前值50,期望更新為100,
這時(shí)候線程3成功執(zhí)行,余額變?yōu)?00,
線程2從Block中恢復(fù),獲取到的也是100,compare之后,繼續(xù)更新余額為50?。。?br /> 此時(shí)可以看到,實(shí)際余額應(yīng)該為100(100-50 50),但是實(shí)際上變?yōu)榱?0(100-50 50-50)這就是ABA問題帶來的成功提交。
循環(huán)時(shí)間長(zhǎng)開銷大
這種循環(huán)也稱為自旋
只能保證一個(gè)共享變量的原子操作
CAS的應(yīng)用
-
樂觀鎖總是假設(shè)最好的情況,每次去操作數(shù)據(jù)都認(rèn)為不會(huì)被別的線程修改數(shù)據(jù),所以在每次操作數(shù)據(jù)的時(shí)候都不會(huì)給數(shù)據(jù)加鎖,即在線程對(duì)數(shù)據(jù)進(jìn)行操作的時(shí)候,別的線程不會(huì)阻塞仍然可以對(duì)數(shù)據(jù)進(jìn)行操作,只有在需要更新數(shù)據(jù)的時(shí)候才會(huì)去判斷數(shù)據(jù)是否被別的線程修改過,如果數(shù)據(jù)被修改過則會(huì)拒絕操作并且返回錯(cuò)誤信息給用戶。
-
悲觀鎖總是假設(shè)最壞的情況,每次去操作數(shù)據(jù)時(shí)候都認(rèn)為會(huì)被的線程修改數(shù)據(jù),所以在每次操作數(shù)據(jù)的時(shí)候都會(huì)給數(shù)據(jù)加鎖,讓別的線程無法操作這個(gè)數(shù)據(jù),別的線程會(huì)一直阻塞直到獲取到這個(gè)數(shù)據(jù)的鎖。這樣的話就會(huì)影響效率,比如當(dāng)有個(gè)線程發(fā)生一個(gè)很耗時(shí)的操作的時(shí)候,別的線程只是想獲取這個(gè)數(shù)據(jù)的值而已都要等待很久。