Java 之 CAS 原理及實現是怎樣的?



面試官今天我們來聊聊CAS吧?你對CAS瞭解多少?

候選者:好,CAS的全稱爲compare and swap,比較並交換

候選者:雖然翻譯過來是「比較並交換」,但它是一個原子性的操作,對應到CPU指令爲cmpxchg

面試官:好傢伙,CPU指令你都知道?

候選者:這沒什麼,都是背的。

面試官:….

候選者:回到CAS上吧,CAS的操作其實非常簡單。

候選者:CAS 有三個操作數:當前值A、內存值V、要修改的新值B

候選者:假設 當前值A 跟 內存值V 相等,那就將 內存值V 改成B

候選者:假設 當前值A 跟 內存值V 不相等,要麼就重試,要麼就放棄更新

候選者:將當前值與內存值進行對比,判斷是否有被修改過,這就是CAS的核心

 

 

面試官:確實,那爲什麼要用CAS呢?

候選者:嗯,要講到CAS就不得不說synchronized鎖了,它是Java鎖…然後…

面試官:稍微打斷一下,synchronized鎖你稍微講下就好了,後面會專門問的,在這不用細講。

候選者:ok,其實就是synchronized鎖每次只會讓一個線程去操作共享資源

候選者而CAS相當於沒有加鎖,多個線程都可以直接操作共享資源,在實際去修改的時候纔去判斷能否修改成功

候選者:在很多的情況下會synchronized鎖要高效很多

候選者:比如,對一個值進行累加,就沒必要使用synchronized鎖,使用juc包下的Atomic類就足以。

面試官:瞭解,那你知道CAS會有什麼缺點嗎?

候選者CAS有個缺點就是會帶來ABA的問題

候選者:從CAS更新的時候,我們可以發現它只比對當前值和內存值是否相等,這會帶來個問題,下面我舉例說明下:

候選者:假設線程A讀到當前值是10,可能線程B把值修改爲100,然後線程C又把值修改爲10。

候選者:等到線程A拿到執行權時,因爲當前值和內存值是一致的,線程A是可以修改的!

候選者:站在線程A的角度來說,這個值是從未被修改的(:

候選者:這是不合理的,因爲我們從上帝的角度來看,這個變量已經被線程B和線程C修改過了。

候選者:這就是所謂的ABA問題

 

 

候選者:要解決ABA的問題,Java也提供了AtomicStampedReference類供我們用,說白了就是加了個版本,比對的就是內存值+版本是否一致

面試官:嗯,瞭解。

面試官阿里巴巴開發手冊提及到 推薦使用 LongAdder 對象,比 AtomicLong 性能更好(減少樂觀鎖的重試次數),你能幫我解讀一下嗎

候選者:AtomicLong做累加的時候實際上就是多個線程操作同一個目標資源

候選者:在高併發時,只有一個線程是執行成功的,其他的線程都會失敗,不斷自旋(重試),自旋會成爲瓶頸

候選者:而LongAdder的思想就是把要操作的目標資源「分散」到數組Cell中

候選者:每個線程對自己的 Cell 變量的 value 進行原子操作,大大降低了失敗的次數

候選者:這就是爲什麼在高併發場景下,推薦使用LongAdder 的原因(:

面試官:OK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章