悲觀鎖:總是假設最壞的情況,每次去拿數據的時候都認為別人會修改,所以每
次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。傳
統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫
鎖等,都是在做操作之前先上鎖。再比如 Java 里面的同步原語 synchronized 關
鍵字的實現也是悲觀鎖。
樂觀鎖:顧名思義,就是很樂觀,每次去拿數據的時候都認為別人不會修改,所
以不會上鎖,但是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,
可以使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣可以提高吞吐量,
像數據庫提供的類似於 write_condition 機制,其實都是提供的樂觀鎖。在 Java
中 java.util.concurrent.atomic 包下面的原子變量類就是使用了樂觀鎖的一種實
現方式 CAS 實現的。
樂觀鎖的實現方式:
1、使用版本標識來確定讀到的數據與提交時的數據是否一致。提交后修改版本標
識,不一致時可以采取丟棄和再次嘗試的策略。
2、java 中的 Compare and Swap 即 CAS ,當多個線程嘗試使用 CAS 同時更新
同一個變量時,只有其中一個線程能更新變量的值,而其它線程都失敗,失敗的
線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。 CAS 操作
中包含三個操作數 —— 需要讀寫的內存位置(V)、進行比較的預期原值(A)
和擬寫入的新值(B)。如果內存位置 V 的值與預期原值 A 相匹配,那么處理器會自
動將該位置值更新為新值 B。否則處理器不做任何操作。
CAS 缺點:
1、ABA 問題:
比如說一個線程 one 從內存位置 V 中取出 A,這時候另一個線程 two 也從內存中
取出 A,並且 two 進行了一些操作變成了 B,然后 two 又將 V 位置的數據變成 A,
這時候線程 one 進行 CAS 操作發現內存中仍然是 A,然后 one 操作成功。盡管線
程 one 的 CAS 操作成功,但可能存在潛藏的問題。從 Java1.5 開始 JDK 的 atomic
包里提供了一個類 AtomicStampedReference 來解決 ABA 問題。
第 180 頁 共 485 頁2、循環時間長開銷大:
對於資源競爭嚴重(線程沖突嚴重)的情況,CAS 自旋的概率會比較大,從而浪
費更多的 CPU 資源,效率低於 synchronized。
3、只能保證一個共享變量的原子操作:
當對一個共享變量執行操作時,我們可以使用循環 CAS 的方式來保證原子操作,
但是對多個共享變量操作時,循環 CAS 就無法保證操作的原子性,這個時候就可
以用鎖。