前言:在對AQS框架進行分析的過程中發現了很多CAS操作,因此有必要對CAS進行一個梳理,也便更清楚的了解其原理。
1.CAS是什么
CAS,是compare and swap的縮寫,中文含義:比較交換。
CAS操作包含三個操作數——需要讀寫的內存值(V)、預期原值[進行比較的值](A)和新值(B)。如果V的值與A值匹配時,那么就將該內存位置的值更新為新值,否則不做任何操作。
CAS用於同步的方式是從地址V處讀取值A,在執行一些列計算后獲得新值B,如果此時內存V處的值和預期值A相等,則將V處值更新為B,此時CAS操作成功。
2.CAS原理
在intel的CPU中,Java CAS操作通過cmpxchg指令來完成原子操作,通過JNI來完成非阻塞算法,J.U.C包都建立在CAS上,對比synchronized阻塞算法,通過CAS在性能上有了很大的提升。
CAS的操作都集中在Unsafe類中,這里看compareAndSwapInt的源碼:
通過源碼可以看出該方法為native的,它會去調底層的C++代碼。對應intel x86處理器源代碼片段:
os::is_MP()命令會根據當前處理器類型來決定是否為cmpxchg指令添加lock前綴。如果是多處理器,就會在cmpxchg指令前加上lock前綴,否則單處理器按書序一致性執行,不需要內存屏障的效果。
3.CAS的缺點
CAS雖然可以很高效的解決原子操作問題,但它仍然存在三大問題。
#1.ABA問題
ABA問題產生的原因:CAS操作的時候需要檢查值有沒有發生變化,如果沒有發生變化則進行更新,但如果一個值原來是A,接着變成B,后來又變成A,在CAS操作的時候會發現它的值沒有發生變化,但實際上值發生了變化。因此這種操作是有問題的。
解決思路:使用版本號。在變量前面追加上版本號,每次變量更新的時候把版本號自增,那么A-B-A 就會變成1A-2B-3A。從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標志是否等於預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。
#2.循環時間開銷大
自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。
#3.只能保證一個共享變量的原子操作
因為Java中的CAS操作只是對CPU的cmpxchg指令的一層封裝,它的功能就是一次只能原子的修改一個變量,可以加鎖來解決這個問題。或者利用AtomicReference把多個變量放入一個對象中進行CAS操作。
總結
對於CAS記住比較交換,相同則更新,以及CAS的在AQS的重要地位。
參考:
https://blog.52itstyle.com/archives/948/
by Shawn Chen, 2019.1.30日,下午。