悲观锁和乐观锁_乐观锁和悲观锁 以及 乐观锁的一种实现方式-CAS
悲觀鎖
總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每次拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞知道它拿到鎖。傳統的關系型數據庫里面就用到了很多的這種鎖機制,比如行鎖,表鎖等,讀鎖、寫鎖等,都是在做操作之前先上鎖。再比如Java里面的同步原語synchronized關鍵字的實現也是悲觀鎖。
樂觀鎖
顧名思義,就是很樂觀,每次去拿數據的時候認為別人不會修改,所以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可以使用版本號機制的。樂觀鎖適用于讀多的應用類型,這樣可以提高吞吐量,像數據庫提供的類似于write_condition機制,其實都 是提供的樂觀鎖。在java中java.util.concurrent.atomic包下面的原子變量就是使用了樂觀鎖的一種實現方式CAS實現的。
Java在JDK1.5之前都是靠synchronized關鍵字保證同步的,這種通過使用一致的鎖定協議來協調對共享狀態的訪問,可以確保無論哪個線程持有共享變量的鎖,都采用獨占的方式來訪問這些變量。這就是一種獨占鎖,獨占鎖其實就 是一種悲觀鎖,所以可以說synchronized是悲觀鎖。
悲觀鎖機制存在以下問題:
1、在多線程競爭下,加鎖、釋放鎖會導致比較多的上下文切換和調度延時,引起性能問題。
2、一個線程持有鎖會導致其他所有需要此鎖的線程掛起。
3、如果一個優先級高的線程等待一個優先級低的線程釋放鎖會導致優先級倒置,引起性能風險。
對比悲觀鎖的這些問題,另一個更加有效的鎖是樂觀鎖。
其實樂觀鎖就:每次不加鎖而是假設沒有并發沖突而去完成某項操作,如果因為并發沖突失敗就重試,直到成功為止。
主要就是兩個步驟:沖突檢測和數據更新
其實現方式有一種比較典型的就是Compare and Swap (CAS)
CAS
CAS 是樂觀鎖技術,當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值,而其他線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試。
JAVA對CAS的支持:
在JDK1.5 中新增 java.util.concurrent (J.U.C)就是建立在CAS之上的。相對于對于 synchronized 這種阻塞算法,CAS是非阻塞算法的一種常見實現。所以J.U.C在性能上有了很大的提升。
CAS存在問題
1. ABA問題:
從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等于預期引用,并且當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。(1A2B3A)
2. 循環時間長開銷大:
自旋CAS(不成功,就一直循環執行,直到成功)如果長時間不成功,會給CPU帶來非常大的執行開銷。
3. 只能保證一個共享變量的原子操作:
當對一個共享變量執行操作時,我們可以使用循環CAS的方式來保證原子操作,但是對多個共享變量操作時,循環CAS就無法保證操作的原子性,這個時候就可以用鎖,或者有一個取巧的辦法,就是把多個共享變量合并成一個共享變量來操作。比如有兩個共享變量i=2,j=a,合并一下ij=2a,然后用CAS來操作ij。從Java1.5開始JDK提供了AtomicReference類來保證引用對象之間的原子性,你可以把多個變量放在一個對象里來進行CAS操作。
CAS與Synchronized的使用情景:
1、對于資源競爭較少(線程沖突較輕)的情況,使用synchronized同步鎖進行線程阻塞和喚醒切換以及用戶態內核態間的切換操作額外浪費消耗cpu資源;而CAS基于硬件實現,不需要進入內核,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的性能。
2、對于資源競爭嚴重(線程沖突嚴重)的情況,CAS自旋的概率會比較大,從而浪費更多的CPU資源,效率低于synchronized。
補充: synchronized在jdk1.6之后,已經改進優化。synchronized的底層實現主要依靠Lock-Free的隊列,基本思路是自旋后阻塞,競爭切換后繼續競爭鎖,稍微犧牲了公平性,但獲得了高吞吐量。在線程沖突較少的情況下,可以獲得和CAS類似的性能;而線程沖突嚴重的情況下,性能遠高于CAS。
總結
以上是生活随笔為你收集整理的悲观锁和乐观锁_乐观锁和悲观锁 以及 乐观锁的一种实现方式-CAS的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 虚拟鼠标代替安卓触屏_iQOO对比黑鲨2
- 下一篇: python怎么自动生成测试报告_pyt