1 悲觀鎖樂觀鎖簡介
樂觀鎖( Optimistic Locking ) 相對悲觀鎖而言,樂觀鎖假設認為數據一般情況下不會造成沖突,所以在數據進行提交更新的時候,才會正式對數據的沖突與否進行檢測,如果發現沖突了,則讓返回用戶錯誤的信息,讓用戶決定如何去做。
悲觀鎖,正如其名,它指的是對數據被外界(包括本系統當前的其他事務,以及來自外部系統的事務處理)修改持保守態度,因此,在整個數據處理過程中,將數據處於鎖定狀態。(百科)
最形象的悲觀鎖 vs 樂觀鎖

2.悲觀鎖樂觀鎖使用場景
兩種鎖各有優缺點,不能單純的定義哪個好於哪個。樂觀鎖比較適合數據修改比較少,讀取比較頻繁的場景,即使出現了少量的沖突,這樣也省去了大量的鎖的開銷,故而提高了系統的吞吐量。但是如果經常發生沖突(寫數據比較多的情況下),上層應用不不斷的retry,這樣反而降低了性能,對於這種情況使用悲觀鎖就更合適。
3.Java中悲觀樂觀鎖實現
樂觀鎖:java中的樂觀鎖基本都是通過CAS操作實現的,CAS是一種更新的原子操作,比較當前值跟傳入值是否一樣,一樣則更新,否則失敗。以 java.util.concurrent 中的 AtomicInteger 為例,該類中原子操作保證了線程訪問的准確性。
getAndIncrement():獲取數據
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(); @Override public 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 job try { Thread.sleep(i * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
compareAndSet(int expect, int update): 更新數據
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指令的操作,訪問寄存器內存數據進行數據訪問和設置
悲觀鎖: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樂觀鎖去獲取鎖,獲取不到,才會轉換為悲觀鎖,如RetreenLock【http://ifeve.com/reentrantlock-and-fairness/】
public class ReentrantLockTest { private static Lock fairLock = new ReentrantLock(true); private static Lock unfairLock = new ReentrantLock(); @Test public 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(); } } @Test public 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; } @Override public void run() { for (int i = 0; i < 5; i++) { lock.lock(); try { System.out.println("Lock by:" + Thread.currentThread().getName()); } finally { lock.unlock(); } } } } }
4 數據庫悲觀鎖樂觀鎖的實現(以mysql為例)
悲觀鎖,使用事務實現
//0.開始事務 begin;/begin work;/start transaction; (三者選一就可以) //1.查詢出商品信息 select status from t_goods where id=1 for update; //2.根據商品信息生成訂單 insert into t_orders (id,goods_id) values (null,1); //3.修改商品status為2 update t_goods set status=2; //4.提交事務 commit;/commit work;
樂觀鎖
1.使用數據版本(Version)記錄機制實現

2.樂觀鎖定的第二種實現方式和第一種差不多,同樣是在需要樂觀鎖控制的table中增加一個字段,名稱無所謂,字段類型使用時間戳(timestamp), 和上面的version類似
5 nosql 悲觀鎖樂觀鎖的實現(以redis為例)
樂觀鎖使用watch

悲觀鎖使用事務
> MULTI OK > INCR foo QUEUED > INCR bar QUEUED > EXEC 1) (integer) 1 2) (integer) 1
6 總結
樂觀鎖機制采取了更加寬松的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨占性。但隨之而來的就是數據庫 性能的大量開銷,特別是對長事務而言,這樣的開銷往往無法承受。相對悲觀鎖而言,樂觀鎖更傾向於開發運用。【百科】
參考資料
【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/