在數據庫操做過程當中,爲了不兩個或多個用戶同時對一條數據操做,一般採用鎖的機制來來解決數據衝突問題。
一樣,在程序流程中爲了不對多線程共享的資源的修改衝突,也採用鎖的機制來避免修改衝突java
所謂樂觀鎖,就是相信大部分場景下,不會產生數據修改衝突,因此在讀取數據進行修改的時候,不對數據進行加鎖,而是在最終提交修改的時候,經過version或CAS機制,檢查對數據的修改是否發生了衝突。
樂觀鎖的適用於讀多寫少的場景,能提供系統的處理能力,若是在衝突比較機率高的場景使用樂觀鎖,反而會下降系統的處理能力。數據庫
所謂悲觀鎖,就是認爲對數據修改發生衝突的機率比較大,因此在讀取數據進行修改的時候,先用「排他寫鎖」鎖住數據,Block其餘人的操做,等修改完成後,再釋放鎖。
此模式比較適用於數據修改衝突發生機率高的場景,但會必定程度下降系統的處理能力。多線程
數據的行鎖、表鎖、讀鎖、寫鎖都屬於悲觀鎖,典型的就是select * from xxx where id=n for update
命令。this
假設有一條訂單(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機制,是在order表中增長一個數字型的version字段,每次查下數據的時候,都帶上version字段。更新數據是,把version字段加1。以上述訂單爲例,好比:對象
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
關鍵字屬於悲觀鎖。
如線程1讀取了一個變量的值爲A;這時候線程2修改變量的值B;線程3有把變量值改回爲A;此時,線程1再去更新此變量,會認爲此變量未被其餘人更新過,但其實變量已經被更新了屢次。
因此CAS是適用於對象子包含單個共享變量的原子操做,對於對象中包含多個共享變量的狀況沒法保證原子性。
對於資源競爭比較激烈的狀況,CAS自旋的機率較大,會致使CPU開銷增大,效率會低於synchronized
。