java多線程——鎖

java多線程有兩種形式的鎖,悲觀鎖和樂觀鎖

java多線程——鎖

悲觀鎖總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖。synchronized、Lock屬於悲觀鎖。Lock有三種實現類:ReentrantLock、ReadLock(讀鎖)和WriteLock(寫鎖)。

樂觀鎖總是假設最好的情況,每次去拿數據的時候都認為別人不會修改,所以不會上鎖。CAS屬於樂觀鎖。

synchronized 與ReentrantLock對比

相同點:synchronized 與ReentrantLock都用於線程同步鎖,都是互斥鎖,即某一時刻只允許一個線程共享資源,不管是讀數據還是寫數據。

不同點:

  • synchronized是內置鎖,只要在代碼開始的地方加synchronized,代碼結束會自動釋放。Lock必須手動加鎖,手動釋放鎖。
  • synchronized代碼量少,自動化,但擴展性低,不夠靈活;ReentrantLock擴展性好,靈活,但代碼量相對多。
  • synchronized釋放鎖:線程執行完畢;線程發生異常;線程進入休眠狀態;Lock釋放鎖:通過unLock方法。
  • synchronized依賴JVM實現,ReentrantLock是JDK實現的。
  • synchronized是非公平鎖,ReentrantLock可以指定是公平鎖還是非公平鎖。所謂的公平鎖就是先等待的線程先獲取鎖。
java多線程——鎖

synchronized代碼塊

java多線程——鎖

ReentrantLock代碼塊

ReentrantLock與ReentrantReadWriteLock對比

ReentrantReadWriteLock實現了ReadWriteLock接口,ReadWriteLock接口中維護了兩個鎖:ReadLock(共享鎖)和writeLock(排他鎖),補充了Lock完成互斥的情況。允許線程同時讀取共享資源;如果有一個線程是寫線程,那麼其他線程就不能讀寫該資源。即會出現三種情況:讀讀共享、寫寫互斥和讀寫互斥。

java多線程——鎖

ReentrantReadWriteLock代碼塊

CAS

CAS是一種無鎖算法,在不使用鎖的的情況下實現多線程之間的變量同步。CAS算法涉及到三個操作數

  • 需要讀寫的內存值V
  • 進行比較的值A
  • 擬寫入的新值B

當且僅當V的值等於A時,CAS通過原子方式使用新值B來更新V的值。如果不相等,則重新獲取內存地址V 的值,並重新計算想要修改的新值。不斷的重試操作。

樂觀鎖適合讀操作多的場景,不加鎖的特點能夠使其讀操作的性能大幅提升。不過樂觀鎖CAS也存在三大問題:

  1. ABA問題:CAS需要在操作值的時候檢查內存值是否發生變化,沒有發生變化才會更新內存值。但是如果內存值原來是A,後來變成了B,然後又變成了A,那麼CAS進行檢查時會發現值沒有發生變化,但是實際上是有變化的。ABA問題的解決思路就是在變量前面添加版本號,每次變量更新的時候都把版本號加1。只有檢查當前引用等於預期引用並且當前版本號等於預期版本號時,才使用原子方式更新給定的新值。
  2. 如果長時間不成功,會給CPU帶來非常大的執行開銷。
  3. 只能保證一個共享變量的原子操作。
java多線程——鎖

CAS代碼塊


分享到:


相關文章: