CAS和Synchronized知识
一. CAS
何為CAS。
CAS(Compare And Swap )是樂觀鎖的一種實現方式,是一種輕量級鎖。JAVA1.5開始引入了CAS,JUC下很多工具類都是基于CAS。
CAS的實現方式
CAS有3個操作數,內存值V,舊的預期值A,要修改的新值B。當且僅當預期值A和內存值V相同時,將內存值V修改為B,否則什么都不做。當多個線程同時嘗試使用CAS更新一個變量時,任何時候只有一個線程可以更新成功,若更新失敗,線程會重新進入循環再次進行嘗試。
CAS在Java中的應用
前面也說了JUC下面很多工具類都用到了CAS。其主要依賴于Unsafe的CAS操作來進行實現。
例如AtomicInteger下的incrementAndGet操作:
接著來看看Unsafe下的getAndAddInt方法
可以看到Unsafe中在循環體內先讀取內存中的value值,然后CAS更新,如果CAS更新成功則退出,如果更新失敗,則循環重試直到更新成功。
CAS帶來的問題
ABA問題
例如說:
一. 線程1查詢值是否為A
二. 線程2查詢值是否為A
三. 線程2使用CAS將值更新為B
四. 線程2查詢值是否為B
五. 線程2使用CAS將值更新為A
六. 線程1使用CAS將值更新為C
線程一線程二交替執行。第二步到第五步,線程二將值由A更新為B由更新為A,但線程一并沒有察覺,因此線程一還是可以繼續執行。我們稱這種現象為ABA問題。
解決方法:
使用版本號 (時間戳),在每次在執行數據的修改操作時,都會帶上一個版本號,一旦版本號和數據的版本號一致就可以執行修改操作并對版本號加一,否則就執行失敗。例如AtomicStampedReference就是通過對值加一個戳(stamp)來解決“ABA”問題的。
循環開銷過大
CAS操作不成功的話,會導致一直自旋,CPU的壓力會很大。例如說Unsafe下的getAndAddInt方法會一直循環,直到成功才會返回。
只能保證一個變量的原子操作
二. synchronized
相比于CAS基于樂觀鎖實現,synchronized是基于悲觀鎖的,當一個線程試圖訪問同步代碼塊時,它首先必須得到鎖,退出或拋出異常時必須釋放鎖。
對于普通同步方法加鎖時,鎖是當前實例對象。
對于靜態同步方法加鎖時,鎖是當前類的Class對象。
對于同步方法塊加鎖時,鎖是Synchonized括號里配置的對象。
Synchronized的實現方式:
Synchonized是基于進入和退出Monitor對象來實現方法同步和代碼塊同步,但兩者的實現細節不一樣。Synchronized 用在方法上時,在字節碼中是通過方法的 ACC_SYNCHRONIZED 標志來實現的。而代碼塊同步則是使用monitorenter和monitorexit指令實現的。
monitorenter指令是在編譯后插入到同步代碼塊的開始位置,而monitorexit是插入到方法結束處和異常處,JVM要保證每個monitorenter必須有對應的monitorexit與之配對。任何對象都有一個monitor與之關聯,當且一個monitor被持有后,它將處于鎖定狀態。線程執行到monitorenter指令時,將會嘗試獲取對象所對應的monitor的所有權,即嘗試獲得對象的鎖,當獲得對象的monitor以后,monitor內部的計數器就會自增(初始為0),當同一個線程再次獲得monitor的時候,計數器會再次自增。當同一個線程執行monitorexit指令的時候,計數器會進行自減,當計數器為0的時候,monitor就會被釋放,其他線程便可以獲得monitor。
Synchronized的優化
Java SE 1.6為了減少獲得鎖和釋放鎖帶來的性能消耗,引入了“偏向鎖”和“輕量級鎖”,在Java SE 1.6中,鎖一共有4種狀態,級別從低到高依次是:無鎖狀態、偏向鎖狀態、輕量級鎖狀態和重量級鎖狀態。
偏向鎖
當一個線程訪問同步塊并獲取鎖時,會在對象頭和棧幀中的鎖記錄里存儲鎖偏向的線程ID,以后該線程在進入和退出同步塊時不需要進行CAS操作來加鎖和解鎖,只需簡單地測試一下對象頭的Mark Word里是否存儲著指向當前線程的偏向鎖。如果測試成功,表示線程已經獲得了鎖。如果測試失敗,則需要再測試一下Mark Word中偏向鎖的標識是否設置成1(表示當前是偏向鎖),如果沒有設置,則使用CAS競爭鎖;如果設置了,則嘗試使用CAS將對象頭的偏向鎖指向當前線程。
輕量級鎖
線程在執行同步塊之前,JVM會先在當前線程的棧楨中創建用于存儲鎖記錄的空間,并將對象頭中的Mark Word復制到鎖記錄中。然后線程嘗試使用CAS將對象頭中的Mark Word替換為指向鎖記錄的指針。如果成功,當前線程獲得鎖,如果失敗,表示其他線程競爭鎖,當前線程便嘗試使用自旋來獲取鎖。
重量級鎖
重量級鎖是依賴對象內部的monitor鎖來實現。當系統檢查到鎖是重量級鎖之后,會把等待想要獲得鎖的線程進行阻塞,被阻塞的線程不會消耗cup。但是阻塞或者喚醒一個線程時,都需要操作系統來幫忙,需要從用戶態轉換到內核態,而轉換狀態是需要消耗很多時間。
總結
以上是生活随笔為你收集整理的CAS和Synchronized知识的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【机器学习】九种顶流回归算法及实例总结
- 下一篇: grep -e命令详解_grep中的正则