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可以指定是公平鎖還是非公平鎖。所謂的公平鎖就是先等待的線程先獲取鎖。
ReentrantLock與ReentrantReadWriteLock對比
ReentrantReadWriteLock實現了ReadWriteLock接口,ReadWriteLock接口中維護了兩個鎖:ReadLock(共享鎖)和writeLock(排他鎖),補充了Lock完成互斥的情況。允許線程同時讀取共享資源;如果有一個線程是寫線程,那麼其他線程就不能讀寫該資源。即會出現三種情況:讀讀共享、寫寫互斥和讀寫互斥。
CAS
CAS是一種無鎖算法,在不使用鎖的的情況下實現多線程之間的變量同步。CAS算法涉及到三個操作數
- 需要讀寫的內存值V
- 進行比較的值A
- 擬寫入的新值B
當且僅當V的值等於A時,CAS通過原子方式使用新值B來更新V的值。如果不相等,則重新獲取內存地址V 的值,並重新計算想要修改的新值。不斷的重試操作。
樂觀鎖適合讀操作多的場景,不加鎖的特點能夠使其讀操作的性能大幅提升。不過樂觀鎖CAS也存在三大問題:
- ABA問題:CAS需要在操作值的時候檢查內存值是否發生變化,沒有發生變化才會更新內存值。但是如果內存值原來是A,後來變成了B,然後又變成了A,那麼CAS進行檢查時會發現值沒有發生變化,但是實際上是有變化的。ABA問題的解決思路就是在變量前面添加版本號,每次變量更新的時候都把版本號加1。只有檢查當前引用等於預期引用並且當前版本號等於預期版本號時,才使用原子方式更新給定的新值。
- 如果長時間不成功,會給CPU帶來非常大的執行開銷。
- 只能保證一個共享變量的原子操作。
閱讀更多 java技術閱讀 的文章