java的CAS

    cas是compare and swap的縮寫,是由操作系統提供的併發操作的原子指令;cas操作包含3個基本值,內存地址,預期值,要更新的值。在更新變量值時,cas會首先將變量內存地址上的原值和預期值相比較,如果相當就將其設置爲要更新的值。cas就是樂觀鎖的一種形式。

    Volatile能保證共享變量的可見性,即線程將共享變量從主內存讀取到線程自己的工作內存中修改後,立即同步到主內存中,v並使其他線程工作內存中的該變量失效,在使用的時候在從主內存中從新讀取。但是volatile不能保證原子性,因此可以利用cas的特性-可見性可原子性,搭配volatile實現併發環境下對共享變量的樂觀鎖。Synchronized是一種獨佔鎖,即悲觀鎖,且Synchronized還涉及到線程的切換等開銷,相比cas性能更差些。

    Java的整個J.UC都是建立在cas的基礎上的,J.U.C的無阻塞隊列都是通過cas實現的。如java.util.concurrent.ConcurrentLinkedQueue。該隊列的各共享變量都是volatile的,每一個對共享變量的設置操作都是通過UNSALF.UNSAFE.compareAndSwapObject實現的,並且由於volatile讀操作快於cas寫,因此該隊列允許head和tail節點延後更新。

    但cas也存在一些問題,主要如下三個:

  1. ABA問題,即線程T1和T2都讀到A值做cas操作,T2將A改爲B在改爲C,T1cas的時候發現自己讀到的值與變量位置上的值一樣,將A改爲B,實際上A位置的值已發生變化。解決這個問題就是加給變量加版本號,1A1B經過T2cas後變爲2A1B,T1去比較時發現版本對不上就不會修改A了。AtomicStampedReference實現了這個機制,exepetcStamp即預期值版本號,newStamp爲要更新的值的版本號:                                                                                                                
  2. 自旋問題,使用cas一般都是在自旋以修改某值,如ConcurrentLinkedQueue的offer()方法中當在尾節點後添加新節點p.casNext(null, newNode)操作時,如果由於併發大,入隊操作會一直自旋,會給CPU帶來非常大的執行開銷。
  3. 只能保證一個共享變量的原子操作。AtomicReference實現了對多個變量的cas修改,將變量放入一個對象中,然後調用AtomicReference的API,cas操作多個共享變量的修改。

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