悲觀鎖
每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改却舀,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖夹厌,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)block直到它拿到鎖列疗。
假定會(huì)發(fā)生并發(fā)沖突,屏蔽一切可能違反數(shù)據(jù)完整性的操作俺附。
Java synchronized 就屬于悲觀鎖的一種實(shí)現(xiàn)肥卡,每次線程要修改數(shù)據(jù)時(shí)都先獲得鎖,保證同一時(shí)刻只有一個(gè)線程能操作數(shù)據(jù)事镣,其他線程則會(huì)被block步鉴。
select - from - where - FOR UPDATE
/**
* 更新庫(kù)存(使用悲觀鎖)
* @param productId
* @return
*/
public boolean updateStock(Long productId){
//先鎖定商品庫(kù)存記錄
ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId} FOR UPDATE", productId);
if (product.getNumber() > 0) {
int updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId}", productId);
if(updateCnt > 0){ //更新庫(kù)存成功
return true;
}
}
return false;
}
樂(lè)觀鎖
每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖璃哟,但是在提交更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù)氛琢。樂(lè)觀鎖適用于讀多寫少的應(yīng)用場(chǎng)景,這樣可以提高吞吐量随闪。
樂(lè)觀鎖:假設(shè)不會(huì)發(fā)生并發(fā)沖突阳似,只在提交操作時(shí)檢查是否違反數(shù)據(jù)完整性。
樂(lè)觀鎖一般來(lái)說(shuō)有以下2種方式:
- 使用數(shù)據(jù)版本(Version)記錄機(jī)制實(shí)現(xiàn)铐伴,這是樂(lè)觀鎖最常用的一種實(shí)現(xiàn)方式撮奏。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個(gè)版本標(biāo)識(shí)当宴,一般是通過(guò)為數(shù)據(jù)庫(kù)表增加一個(gè)數(shù)字類型的 “version” 字段來(lái)實(shí)現(xiàn)畜吊。當(dāng)讀取數(shù)據(jù)時(shí),將version字段的值一同讀出即供,數(shù)據(jù)每更新一次定拟,對(duì)此version值加一于微。當(dāng)我們提交更新的時(shí)候逗嫡,判斷數(shù)據(jù)庫(kù)表對(duì)應(yīng)記錄的當(dāng)前版本信息與第一次取出來(lái)的version值進(jìn)行比對(duì),如果數(shù)據(jù)庫(kù)表當(dāng)前版本號(hào)與第一次取出來(lái)的version值相等株依,則予以更新驱证,否則認(rèn)為是過(guò)期數(shù)據(jù)。
- 使用時(shí)間戳(timestamp)恋腕。樂(lè)觀鎖定的第二種實(shí)現(xiàn)方式和第一種差不多抹锄,同樣是在需要樂(lè)觀鎖控制的table中增加一個(gè)字段,名稱無(wú)所謂荠藤,字段類型使用時(shí)間戳(timestamp), 和上面的version類似伙单,也是在更新提交的時(shí)候檢查當(dāng)前數(shù)據(jù)庫(kù)中數(shù)據(jù)的時(shí)間戳和自己更新前取到的時(shí)間戳進(jìn)行對(duì)比,如果一致則OK哈肖,否則就是版本沖突吻育。
Java 中的atomic包就是樂(lè)觀鎖的一種實(shí)現(xiàn),AtomicInteger 通過(guò)CAS(Compare And Set)操作實(shí)現(xiàn)線程安全的自增淤井。
/**
* 下單減庫(kù)存
* @param productId
* @return
*/
public boolean updateStock(Long productId){
int updateCnt = 0;
while (updateCnt == 0) {
ProductStock product = query("SELECT * FROM tb_product_stock WHERE product_id=#{productId}", productId);
if (product.getNumber() > 0) {
updateCnt = update("UPDATE tb_product_stock SET number=number-1 WHERE product_id=#{productId} AND number=#{number}", productId, product.getNumber());
if(updateCnt > 0){ //更新庫(kù)存成功
return true;
}
} else { //賣完啦
return false;
}
}
return false;
}
//number 作為version, get出來(lái)布疼,update時(shí)檢測(cè)是否一致摊趾。