五分钟学会悲观乐观锁-java vs mysql vs redis三种实现
1 悲觀鎖樂觀鎖簡介
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設(shè)認(rèn)為數(shù)據(jù)一般情況下不會造成沖突,所以在數(shù)據(jù)進(jìn)行提交更新的時候,才會正式對數(shù)據(jù)的沖突與否進(jìn)行檢測,如果發(fā)現(xiàn)沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
悲觀鎖,正如其名,它指的是對數(shù)據(jù)被外界(包括本系統(tǒng)當(dāng)前的其他事務(wù),以及來自外部系統(tǒng)的事務(wù)處理)修改持保守態(tài)度,因此,在整個數(shù)據(jù)處理過程中,將數(shù)據(jù)處于鎖定狀態(tài)。(百科)
最形象的悲觀鎖 vs 樂觀鎖
?
2.悲觀鎖樂觀鎖使用場景
兩種鎖各有優(yōu)缺點,不能單純的定義哪個好于哪個。樂觀鎖比較適合數(shù)據(jù)修改比較少,讀取比較頻繁的場景,即使出現(xiàn)了少量的沖突,這樣也省去了大量的鎖的開銷,故而提高了系統(tǒng)的吞吐量。但是如果經(jīng)常發(fā)生沖突(寫數(shù)據(jù)比較多的情況下),上層應(yīng)用不不斷的retry,這樣反而降低了性能,對于這種情況使用悲觀鎖就更合適。
3.Java中悲觀樂觀鎖實現(xiàn)
樂觀鎖:java中的樂觀鎖基本都是通過CAS操作實現(xiàn)的,CAS是一種更新的原子操作,比較當(dāng)前值跟傳入值是否一樣,一樣則更新,否則失敗。以 java.util.concurrent 中的 AtomicInteger 為例,該類中原子操作保證了線程訪問的準(zhǔn)確性。
getAndIncrement():獲取數(shù)據(jù)
import java.util.concurrent.atomic.AtomicInteger; public class JavaAtomic {public static void main(String[] args) throws InterruptedException {ProcessingThread pt = new ProcessingThread();Thread t1 = new Thread(pt, "t1");t1.start();Thread t2 = new Thread(pt, "t2");t2.start();t1.join();t2.join();System.out.println("Processing count=" + pt.getCount());} } class ProcessingThread implements Runnable {private AtomicInteger count = new AtomicInteger();@Overridepublic void run() {for (int i = 1; i < 5; i++) {processSomething(i);count.incrementAndGet();}}public int getCount() {return this.count.get();}private void processSomething(int i) {// processing some jobtry {Thread.sleep(i * 1000);} catch (InterruptedException e) {e.printStackTrace();}} }?
compareAndSet(int expect, int update): 更新數(shù)據(jù)
import java.util.concurrent.atomic.AtomicInteger;public class Main {public static void main(String[] args){AtomicInteger atomicInteger = new AtomicInteger(100);boolean isSuccess = atomicInteger.compareAndSet(100,110); //current value 100 System.out.println(isSuccess); //true isSuccess = atomicInteger.compareAndSet(100,120); //current value 110 System.out.println(isSuccess); //false } }?
利用JNI(Java Native Interface)來完成CPU指令的操作,訪問寄存器內(nèi)存數(shù)據(jù)進(jìn)行數(shù)據(jù)訪問和設(shè)置
悲觀鎖:java中的悲觀鎖就是Synchronized,如單例模式所示
public class SingletonDemo {private static SingletonDemo instance = null;private SingletonDemo() { }public static synchronized SingletonDemo getInstance() {if (instance == null) {instance = new SingletonDemo ();}return instance;} }?
樂觀鎖+悲觀鎖:AQS框架下的鎖則是先嘗試cas樂觀鎖去獲取鎖,獲取不到,才會轉(zhuǎn)換為悲觀鎖,如RetreenLock【http://ifeve.com/reentrantlock-and-fairness/】
public class ReentrantLockTest {private static Lock fairLock = new ReentrantLock(true);private static Lock unfairLock = new ReentrantLock();@Testpublic void fair() {System.out.println("fair version");for (int i = 0; i < 5; i++) {Thread thread = new Thread(new Job(fairLock));thread.setName("" + i);thread.start();}try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}@Testpublic void unfair() {System.out.println("unfair version");for (int i = 0; i < 5; i++) {Thread thread = new Thread(new Job(unfairLock));thread.setName("" + i);thread.start();}try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}}private static class Job implements Runnable {private Lock lock;public Job(Lock lock) {this.lock = lock;}@Overridepublic void run() {for (int i = 0; i < 5; i++) {lock.lock();try {System.out.println("Lock by:"+ Thread.currentThread().getName());} finally {lock.unlock();}}}} }?
4 數(shù)據(jù)庫悲觀鎖樂觀鎖的實現(xiàn)(以mysql為例)
悲觀鎖,使用事務(wù)實現(xiàn)
//0.開始事務(wù) begin;/begin work;/start transaction; (三者選一就可以) //1.查詢出商品信息 select status from t_goods where id=1 for update; //2.根據(jù)商品信息生成訂單 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status為2 update t_goods set status=2; //4.提交事務(wù) commit;/commit work;?
樂觀鎖
1.使用數(shù)據(jù)版本(Version)記錄機(jī)制實現(xiàn)
?
2.樂觀鎖定的第二種實現(xiàn)方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version類似
5 nosql 悲觀鎖樂觀鎖的實現(xiàn)(以redis為例)
樂觀鎖使用watch
?
悲觀鎖使用事務(wù)
> MULTI OK > INCR foo QUEUED > INCR bar QUEUED > EXEC 1) (integer) 1 2) (integer) 1?
6 總結(jié)
樂觀鎖機(jī)制采取了更加寬松的加鎖機(jī)制。悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機(jī)制實現(xiàn),以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫 性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。相對悲觀鎖而言,樂觀鎖更傾向于開發(fā)運用。【百科】
參考資料
【1】https://chenzhou123520.iteye.com/blog/1860954
【2】https://chenzhou123520.iteye.com/blog/1863407
【3】https://blog.csdn.net/skycnlr/article/details/85689582
【4】https://www.journaldev.com/1095/atomicinteger-java
【5】https://howtodoinjava.com/java/multi-threading/atomicinteger-example/
【6】https://developpaper.com/transaction-mechanism-and-optimistic-lock-implementation-in-redis/
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/11383525.html
總結(jié)
以上是生活随笔為你收集整理的五分钟学会悲观乐观锁-java vs mysql vs redis三种实现的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 调试应用不发愁,免安装的 curl 来帮
- 下一篇: 淘宝大数据之路【转】