在開始問題闡述之前呢,我們先看一則小故事:
在開始問題的闡述之前,我們先來看一則小故事:
北宋宋真宗皇后死後,當時他的兩位愛妃劉妃和李妃都懷了孕,很顯然,誰生了兒子,誰就有可能立為正宮。劉妃久懷嫉妒之心,唯恐李妃生了兒子被立為皇后,於是與宮中總管都堂郭槐定計,在接生婆尤氏的配合下,乘李妃分娩時由於血暈而人事不知之機,將一狸貓剝去皮毛,血淋淋,光油油地換走了剛出世的太子。劉妃命宮女寇珠勒死太子,寇珠於心不忍,暗中將太子交付宦官陳琳,陳琳將太子裝在提盒中送至八賢王處撫養。再說真宗看到被剝了皮的狸貓,以為李妃產下了乙個妖物,乃將其貶入冷宮。不久,劉妃臨產,生了個兒子,被立為太子,劉妃也被冊立為皇后。誰知六年後,劉後之子病夭。真宗再無子嗣,就將其皇兄八賢王之子(實為當年被換走的皇子)收為義子,並立為太子。從這故事看出來,太子出生被被換成了狸貓,最後陰差陽錯又回歸成為太子。雖然結果是一樣的,但是過程曲折了,太子真的是命途多舛啊。
為什麼說這個故事?其實跟我們今天要介紹的問題有很大的關係。同樣的結果,可能中間不知道發生了多少次操作,那麼我們能認為他沒變嗎?在不同的業務場景下,我們要仔細考慮這個問題。
在多執行緒場景下cas
會出現aba
問題,關於aba問題這裡簡單科普下,例如有2個執行緒同時對同乙個值(初始值為a)進行cas操作,這三個執行緒如下:
執行緒1,期望值為a,欲更新的值為b
執行緒2,期望值為a,欲更新的值為b
執行緒1搶先獲得cpu時間片,而執行緒2因為其他原因阻塞了,執行緒1取值與期望的a值比較,發現相等然後將值更新為b,然後這個時候出現了執行緒3,期望值為b,欲更新的值為a,執行緒3取值與期望的值b比較,發現相等則將值更新為a,此時執行緒2從阻塞中恢復,並且獲得了cpu時間片,這時候執行緒2取值與期望的值a比較,發現相等則將值更新為b,雖然執行緒2也完成了操作,但是執行緒2並不知道值已經經過了a->b->a的變化過程。
小明在提款機,提取了50元,因為提款機問題,有兩個執行緒,同時把餘額從100變為50:
此時可以看到,實際餘額應該為100(100-50+50)
,但是實際上變為了50(100-50+50-50)
這就是aba問題帶來錯誤提交結果。
要解決aba問題,可以增加乙個版本號,當記憶體位置v的值每次被修改後,版本號都加1。
private
static atomicstampedreference
atomicstampedreference =
newatomicstampedreference
(100,1
);public
static
void
main
(string[
] args)
catch
(interruptedexception e)
atomicstampedreference.
compareandset
(100
,101
,atomicstampedreference.
getstamp()
,atomicstampedreference.
getstamp()
+1);
atomicstampedreference.
compareandset
(101
,100
,atomicstampedreference.
getstamp()
,atomicstampedreference.
getstamp()
+1);
},"t1").
start()
;// 第二個執行緒
newthread((
)->
catch
(interruptedexception e)
system.out.
println
("最新版本號:"
+ atomicstampedreference.
getstamp()
);system.out.
println
(atomicstampedreference.
compareandset
(100
,2019
,stamp,atomicstampedreference.
getstamp()
+1)+
"\t當前值:"
+ atomicstampedreference.
getreference()
);},
"t2").
start()
;}
1、初始值100,初始版本號1執行結果:2、執行緒t1和t2拿到一樣的初始版本號
3、執行緒t1完成aba操作,版本號遞增到3
4、執行緒t2完成cas操作,最新版本號已經變成3,跟執行緒t2之前拿到的版本號1不相等,操作失敗
t1拿到的初始版本號:1
t2拿到的初始版本號:1
最新版本號:3
false 當前值:100
atomicstampedreference
可以給引用加上版本號,追蹤引用的整個變化過程,如:a -> b -> c -> d -> a,通過atomicstampedreference,我們可以知道,引用變數中途被更改了3次。
但是,有時候,我們並不關心引用變數更改了幾次,只是單純的關心是否更改過,所以就有了atomicmarkablereference
,
atomicmarkablereference的唯一區別就是不再用int標識引用,而是使用boolean變數——表示引用變數是否被更改過。
private
static atomicmarkablereference
atomicmarkablereference =
newatomicmarkablereference
(100
,false);
public
static
void
main
(string[
] args)
catch
(interruptedexception e)
atomicmarkablereference.
compareandset
(100
,101
,atomicmarkablereference.
ismarked()
,true);
atomicmarkablereference.
compareandset
(101
,100
,atomicmarkablereference.
ismarked()
,true);
},"t1").
start()
;// 第二個執行緒
newthread((
)->
catch
(interruptedexception e)
system.out.
println
("是否更改過:"
+ atomicmarkablereference.
ismarked()
);system.out.
println
(atomicmarkablereference.
compareandset
(100
,2019
,ismarked,
true)+
"\t當前值:"
+ atomicmarkablereference.
getreference()
);},
"t2").
start()
;}
1、初始值100,初始版本號未被修改 false執行結果:2、執行緒t1和t2拿到一樣的初始版本號都未被修改 false
3、執行緒t1完成aba操作,版本號被修改 true
4、執行緒t2完成cas操作,版本號已經變成true,跟執行緒t2之前拿到的版本號false不相等,操作失敗
t1版本號是否被更改:false
t2版本號是否被更改:false
是否更改過:true
false 當前值:100
以上是本期關於cas領域的乙個經典aba問題的解析,不知道你在實際的工作中有沒有遇到過,但是在面試中這塊是併發知識考查的重點。如果你還沒接觸過此類的問題,我的建議是你自己將上面的**執行一下,結合理論去理解一下aba問題所帶來的問題以及如何解決他,這對你日後的開發工作也是有莫大的幫助的!
摘錄 面試官也許會這樣問你
回答樣本一 我對工資沒有硬性要求,我相信貴公司在處理我的問題上會友善合理。我注重的是找對工作機會,所以只要條件公平,我則不會計較太多。回答樣本二 我受過系統的軟體程式設計的訓練,不需要進行大量的培訓,而且我本人也對程式設計特別感興趣。因此,我希望公司能根據我的情況和市場標準的水平,給我合理的薪水。回...
面試官問題
解釋一些壓縮感知理論 壓縮感知是一種全新的訊號採集和恢復理論,它打破了傳統的奈奎斯特取樣定律,可以從更少的觀測訊號訊號中重建出高質量的原始訊號。其基本思想是,當訊號是稀疏或可壓縮時,我們可以通過乙個隨機測量矩陣將高維訊號投影到低維空間,再通過乙個求解優化問題高概率的恢復原始訊號。解釋一下壓縮感知磁共...
當面試官問 你還有什麼想問的嗎 時,應該問什麼
經常面試別人,聽到各種不同的版本,最後這個環節,hr很有心機的問出這個問題,每個人的反應都是不一樣的。先舉乙個差點要被錄取,回答完這個問題後直接送走的勵志故事,這位仁兄是這樣問的 公司加班嚴重嗎?加班費怎麼算,這個我覺得還是說明白好。耐心回答下 聽說網際網路公司現在流行境外旅遊的福利,咱們也是網際網...