CAS英文解釋是比較和交換,是cpu底層的源語,是解決共享變量原子性實現方案,它定義了三個變量,內存地址值對應V,期待值E和要修改的值U,以下圖所示,這些變量都是在高速緩存中的,若是兩個線程A,B分別經過cas方式同時修改共享變量,假設當A線程先獲取時間片,若是發現V的值和E相等就將主內存值更新爲U,若是不相等說明線程B在線程A更新以前已經成功更新過,線程A會失敗重試,此時根據緩存一致性協議,線程A的本地副本會失效,須要從主內存再同步最新的變量到本地內存副本,在Java中經過調用UnSafe的compareAndSet相似方式調用,底層是c,反編譯後操做系統指令是cmpxchg指令。面試
你必定會有一個疑問,被volatile修飾的變量i,i++爲何會有線程安全問題呢,也就是原子性的問題,咱們仍是舉一個經典的i++案例一步步分析吧!咱們知道在多線程狀況下volatile保證了共享變量的可見性,順序行,但惟獨不能保證原子性,緣由是i++是一個複合操做,大體能夠分紅3步,1.先從主內存拿到最新的i值,2.將i加1這個操做保存到操做數棧,3.從棧中取出i加1的值寫回到主內存。OK,當線程AB同時執行i++操做時,好比線程A先獲取時間片,執行完第2步,這是線程A還未執行完,時間片分配給線程B,B順利執行完全部操做後並同步了主內存,假設咱們i的初始值是1,那麼此時主內存值是2,由於線程B執行完畢,cpu時間片又回到線程A手上,作第3步操做,此時同步到主內存的值仍是2,看,線程A,B各作了一次加1的操做,但最終結果多是2,cas的做用就來了,他能保證i++操做的原子性,爲何能保證原子性呢?cas能夠把上面三個操做合併成一個操做,是原子的。緩存
你們都知道解決多線程安全須要用到鎖的,能夠用synchronized來解決,可是synchronized也有它的劣勢,最主要是它是阻塞的,阻塞會有什麼問題?性能啊,這是計算機人不能忍的,頻繁內核外核切換,會嚴重浪費系統資源,因此就提了cas這個樂觀鎖概念,它是非阻塞的,操做系統不用在內核態與用戶態來回切換,至關於用while循環方式獲取鎖,在性能上有必定提高。即便這樣,也會有必定問題,下面咱們來看看。安全
1.ABA問題。多線程
這個案例比較簡單,線程A把共享變量i,從1變成2,再變成1,線程B想把i變成2,原本應該是不會成功,由於即時變量i如今是1,可是它的狀態變化了,他的解決方案是版本號。至關於修改爲功一次版本號增長1,就能夠解決了,曾經被面試官問到一個問題,cas是線程安全的嗎?答案不是線程安全的。併發
2.自旋時間過長。ide
若是一個線程拿到鎖後,一直不釋放,其餘線程就只能一直循環等待。性能
3.只能保證一個共享變量的原子性。spa
像Automic包下面的基本上都只能保證一個變量的原子性。操作系統
可能有些童鞋看JDK源碼會比較糾結一個點,發現volatile關鍵字通常都會和cas連用,若是不要volatile會怎麼樣呢?cas自己只做用於方法,cas對共享變量沒有約束,若是不對共享變量作volatile修飾,是不可見的,不可以保證共享變量的實效性,須要等待共享變量主動同步到主內存,這是須要花時間的,效率更低下,全部在JUC併發包裏一直能夠看到這樣的volatile關鍵字通常都會和cas組合。線程
這篇文章,咱們先引出了cas概念,而且說明了它的優缺點,作了案例介紹,簡單的和synchronized關鍵字作了比較,最後,深刻的說明了volatile關鍵字和cas連用的效率,這是我在深刻思考後獲得的結論,分享給你們,文章有必定閱讀門檻,若是有想搞清楚童鞋,能夠1v1私聊討論交流。但願你們喜歡。點贊哦!
我是叫練,邊叫邊練,歡迎點贊和評論。