樂觀鎖和悲觀鎖

鎖機制

在數據庫操做過程當中,爲了不兩個或多個用戶同時對一條數據操做,一般採用鎖的機制來來解決數據衝突問題。
一樣,在程序流程中爲了不對多線程共享的資源的修改衝突,也採用鎖的機制來避免修改衝突java

鎖的分類

樂觀鎖(Optimistic Lock)

所謂樂觀鎖,就是相信大部分場景下,不會產生數據修改衝突,因此在讀取數據進行修改的時候,不對數據進行加鎖,而是在最終提交修改的時候,經過version或CAS機制,檢查對數據的修改是否發生了衝突。
樂觀鎖的適用於讀多寫少的場景,能提供系統的處理能力,若是在衝突比較機率高的場景使用樂觀鎖,反而會下降系統的處理能力。數據庫

悲觀鎖(Pessimistic Lock)

所謂悲觀鎖,就是認爲對數據修改發生衝突的機率比較大,因此在讀取數據進行修改的時候,先用「排他寫鎖」鎖住數據,Block其餘人的操做,等修改完成後,再釋放鎖。
此模式比較適用於數據修改衝突發生機率高的場景,但會必定程度下降系統的處理能力。多線程

數據庫場景

悲觀鎖

數據的行鎖、表鎖、讀鎖、寫鎖都屬於悲觀鎖,典型的就是select * from xxx where id=n for update命令。this

樂觀鎖

CAS機制(compare and swap)

假設有一條訂單(order)數據,ID爲1,訂單狀態(status)是已付款,這個時候,商家打開訂單列表,準備進行發貨;在商家打開訂單後,這時候用戶在APP端取消了訂單,可是商家不知道;商家執行發貨的時候;這時候商家操做發貨,若是隻根據ID進行更新:atom

update order set status='已發貨' where id=1

則會致使取消的訂單被髮貨,此時,使用CAS機制,在更新數據的時候檢查訂單狀態是否正確:線程

update order set status='已發貨' where id=1 and status='已付款'

並經過檢查update語句發返回值,能夠確認時數據更新是否成功。code

Version機制

Version機制,是在order表中增長一個數字型的version字段,每次查下數據的時候,都帶上version字段。更新數據是,把version字段加1。以上述訂單爲例,好比:對象

  1. 訂單建立後version爲1;
  2. 付款後version爲2;
  3. 此時商家準備發貨,讀到的version爲2;
  4. 用戶取消訂單後,version爲3;
  5. 商家發貨是,更新訂單狀態是,發現version不是讀取時的2,說明訂單已經被更新,系統駁回商家的修改,並提高商家。

JAVA鎖場景

Java中, java.util.concurrent.atomic包下的原子變量屬於使用CAS計算的樂觀鎖。內存

public class AtomicInteger extends Number implements java.io.Serializable { 
  private volatile int value; 
 
  public final int get() { 
    return value; 
  } 
 
  public final int getAndIncrement() { 
    for (;;) { 
      int current = get(); 
      int next = current + 1; 
      if (compareAndSet(current, next)) 
        return current; 
    } 
  } 
 
  public final boolean compareAndSet(int expect, int update) { 
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
  } 
}

getAndIncrement 採用了CAS機制,每次從內存中讀取數據,而後將此數據和 +1 後的結果進行CAS操做,若是成功就返回結果,不然重試直到成功爲止。
compareAndSet 利用JNI來完成CPU指令的操做:資源

public final boolean compareAndSet(int expect, int update) {  
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
} 

unsafe.compareAndSwapInt(this, valueOffset, expect, update)邏輯相似以下:

if (this == expect) {
   this = update
   return true;
 } else {
   return false;
 }

synchronized關鍵字屬於悲觀鎖。

CAS的問題

ABA問題

如線程1讀取了一個變量的值爲A;這時候線程2修改變量的值B;線程3有把變量值改回爲A;此時,線程1再去更新此變量,會認爲此變量未被其餘人更新過,但其實變量已經被更新了屢次。
因此CAS是適用於對象子包含單個共享變量的原子操做,對於對象中包含多個共享變量的狀況沒法保證原子性。

鎖開銷

對於資源競爭比較激烈的狀況,CAS自旋的機率較大,會致使CPU開銷增大,效率會低於synchronized

相關文章
相關標籤/搜索