您好,登錄后才能下訂單哦!
這篇文章主要為大家分析了redis實現分布式重入鎖的方法是什么的相關知識點,內容詳細易懂,操作細節合理,具有一定參考價值。如果感興趣的話,不妨跟著跟隨小編一起來看看,下面跟著小編一起深入學習“redis實現分布式重入鎖的方法是什么”的知識吧。
即若當前線程執行某個方法已經獲取了該鎖,那么在方法中嘗試再次獲取鎖時,就會獲取不到而阻塞。
可重入鎖,也叫做遞歸鎖,指的是在同一線程內,外層函數獲得鎖之后,內層遞歸函數仍然可以獲取到該鎖。 就是同一個線程再次進入同樣代碼時,可以再次拿到該鎖。
防止在同一線程中多次獲取鎖而導致死鎖發生。
注:在java的編程中synchronized 和 ReentrantLock都是可重入鎖。
步驟1:雙重加鎖邏輯
public class SynchronizedDemo { //模擬庫存100 int count=100; public synchronized void operation(){ log.info("第一層鎖:減庫存"); //模擬減庫存 count--; add(); log.info("下訂單結束庫存剩余:{}",count); } private synchronized void add(){ log.info("第二層鎖:插入訂單"); try { Thread.sleep(1000*10); } catch (InterruptedException e) { e.printStackTrace(); } } }
步驟2:加個測試類
public static void main(String[] args) { SynchronizedDemo synchronizedDemo=new SynchronizedDemo(); for (int i = 0; i < 3; i++) { int finalI = i; new Thread(()->{ log.info("-------用戶{}開始下單--------", finalI); synchronizedDemo.operation(); }).start(); } }
步驟3:測試
20:44:04.013 [Thread-2] INFO com.agan.redis.controller.SynchronizedController - -------用戶2開始下單-------- 20:44:04.013 [Thread-1] INFO com.agan.redis.controller.SynchronizedController - -------用戶1開始下單-------- 20:44:04.013 [Thread-0] INFO com.agan.redis.controller.SynchronizedController - -------用戶0開始下單-------- 20:44:04.016 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一層鎖:減庫存 20:44:04.016 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二層鎖:插入訂單 20:44:14.017 [Thread-2] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下訂單結束庫存剩余:99 20:44:14.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一層鎖:減庫存 20:44:14.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二層鎖:插入訂單 20:44:24.017 [Thread-0] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下訂單結束庫存剩余:98 20:44:24.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第一層鎖:減庫存 20:44:24.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 第二層鎖:插入訂單 20:44:34.017 [Thread-1] INFO com.agan.redis.Reentrant.SynchronizedDemo - 下訂單結束庫存剩余:97
由于synchronized關鍵字修飾的是方法,所有加鎖為實例對象:synchronizedDemo
運行結果可以看出減庫存和插入訂單都是每個線程都完整運行兩個方法完畢,才能釋放鎖,其他線程才能拿鎖,即是一個線程多次可以拿到同一個鎖,可重入。所以synchronized也是可重入鎖。
ReentrantLock,是一個可重入且獨占式的鎖,是一種遞歸無阻塞的同步鎖。和synchronized關鍵字相比,它更靈活、更強大,增加了輪詢、超時、中斷
等高級功能。
步驟1:雙重加鎖邏輯
public class ReentrantLockDemo { private Lock lock = new ReentrantLock(); public void doSomething(int n){ try{ //進入遞歸第一件事:加鎖 lock.lock(); log.info("--------遞歸{}次--------",n); if(n<=2){ try { Thread.sleep(1000*2); } catch (InterruptedException e) { e.printStackTrace(); } this.doSomething(++n); }else{ return; } }finally { lock.unlock(); } } }
步驟2:加個測試類
public static void main(String[] args) { ReentrantLockDemo reentrantLockDemo=new ReentrantLockDemo(); for (int i = 0; i < 3; i++) { int finalI = i; new Thread(()->{ log.info("-------用戶{}開始下單--------", finalI); reentrantLockDemo.doSomething(1); }).start(); } }
步驟3:測試
20:55:23.533 [Thread-1] INFO com.agan.redis.controller.ReentrantController - -------用戶1開始下單-------- 20:55:23.533 [Thread-2] INFO com.agan.redis.controller.ReentrantController - -------用戶2開始下單-------- 20:55:23.533 [Thread-0] INFO com.agan.redis.controller.ReentrantController - -------用戶0開始下單-------- 20:55:23.536 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸1次-------- 20:55:25.537 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸2次-------- 20:55:27.538 [Thread-1] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸3次-------- 20:55:27.538 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸1次-------- 20:55:29.538 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸2次-------- 20:55:31.539 [Thread-2] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸3次-------- 20:55:31.539 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸1次-------- 20:55:33.539 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸2次-------- 20:55:35.540 [Thread-0] INFO com.agan.redis.Reentrant.ReentrantLockDemo - --------遞歸3次--------
運行結果可以看出,每個線程都可以多次加鎖解鎖的,ReentrantLock是可重入的。
setnx雖然可以實現分布式鎖,但是不可重入,在一些復雜的業務場景,我們需要分布式重入鎖時,
對于redis的重入鎖業界還是有很多解決方案的,目前最流行的就是采用Redisson
。【相關推薦:Redis視頻教程】
什么是Redisson?
Redisson是Redis官方推薦的Java版的Redis客戶端。
基于Java實用工具包中常用接口,為使用者提供了一系列具有分布式特性的常用工具類。
在網絡通信上是基于NIO的Netty框架,保證網絡通信的高性能。
在分布式鎖的功能上,它提供了一系列的分布式鎖;如:
可重入鎖(Reentrant Lock)
公平鎖(Fair Lock)
非公平鎖(unFair Lock)
讀寫鎖(ReadWriteLock)
聯鎖(MultiLock)
紅鎖(RedLock)
案例實戰:體驗redis分布式重入鎖
步驟1:Redisson配置
Redisson配置的可以查考:redis分布式緩存(三十四)一一 SpringBoot整合Redission - 掘金 (juejin.cn)
https://juejin.cn/post/7057132897819426824
步驟2:Redisson重入鎖測試類
public class RedisController { @Autowired RedissonClient redissonClient; @GetMapping(value = "/lock") public void get(String key) throws InterruptedException { this.getLock(key, 1); } private void getLock(String key, int n) throws InterruptedException { //模擬遞歸,3次遞歸后退出 if (n > 3) { return; } //步驟1:獲取一個分布式可重入鎖RLock //分布式可重入鎖RLock :實現了java.util.concurrent.locks.Lock接口,同時還支持自動過期解鎖。 RLock lock = redissonClient.getLock(key); //步驟2:嘗試拿鎖 // 1. 默認的拿鎖 //lock.tryLock(); // 2. 支持過期解鎖功能,10秒鐘以后過期自動解鎖, 無需調用unlock方法手動解鎖 //lock.tryLock(10, TimeUnit.SECONDS); // 3. 嘗試加鎖,最多等待3秒,上鎖以后10秒后過期自動解鎖 // lock.tryLock(3, 10, TimeUnit.SECONDS); boolean bs = lock.tryLock(3, 10, TimeUnit.SECONDS); if (bs) { try { // 業務代碼 log.info("線程{}業務邏輯處理: {},遞歸{}" ,Thread.currentThread().getName(), key,n); //模擬處理業務 Thread.sleep(1000 * 5); //模擬進入遞歸 this.getLock(key, ++n); } catch (Exception e) { log.error(e.getLocalizedMessage()); } finally { //步驟3:解鎖 lock.unlock(); log.info("線程{}解鎖退出",Thread.currentThread().getName()); } } else { log.info("線程{}未取得鎖",Thread.currentThread().getName()); } } }
RLock三個加鎖動作:
lock.tryLock();
默認的拿鎖
lock.tryLock(10, TimeUnit.SECONDS);
支持過期解鎖功能,10秒鐘以后過期自動解鎖
lock.tryLock(3, 10, TimeUnit.SECONDS);
嘗試加鎖,最多等待3秒,上鎖以后10秒后過期自動解鎖
區別:
lock.lock():阻塞式等待。默認加的鎖都是30s
鎖的自動續期,如果業務超長,運行期間自動鎖上新的30s。不用擔心業務時間長而導致鎖自動過期被刪掉(默認續期)
加鎖的業務只要運行完成,就不會給當前鎖續期,即使不手動解鎖,鎖默認會在30s內自動過期,不會產生死鎖問題
lock()如果我們未指定鎖的超時時間,就使用【看門狗默認時間】: lockWatchdogTimeout = 30 * 1000
原理:只要占鎖成功,就會啟動一個定時任務【重新給鎖設置過期時間,新的過期時間就是看門狗的默認時間】,每隔10秒都會自動的再次續期,續成30秒
lock.lock(10,TimeUnit.SECONDS) :10秒鐘自動解鎖,自動解鎖時間一定要大于業務執行時間
出現的問題:在鎖時間到了以后,不會自動續期
原理:lock(10,TimeUnit.SECONDS)如果我們傳遞了鎖的超時時間,就發送給redis執行腳本,進行占鎖,默認超時就是我們制定的時間
最佳實戰:
lock.lock(10,TimeUnit.SECONDS); 省掉看門狗續期操作,自動解鎖時間一定要大于業務執行時間,手動解鎖
步驟3:測試
訪問3次:http://127.0.0.1:9090/lock?key=ljw
線程http-nio-9090-exec-1業務邏輯處理: ljw,遞歸1 線程http-nio-9090-exec-2未取得鎖 線程http-nio-9090-exec-1業務邏輯處理: ljw,遞歸2 線程http-nio-9090-exec-3未取得鎖 線程http-nio-9090-exec-1業務邏輯處理: ljw,遞歸3 線程http-nio-9090-exec-1解鎖退出 線程http-nio-9090-exec-1解鎖退出 線程http-nio-9090-exec-1解鎖退出
通過測試結果:
nio-9090-exec-1線程,在getLock方法遞歸了3次,即證明了lock.tryLock是可重入鎖
只有當nio-9090-exec-1線程執行完后,io-9090-exec-2 nio-9090-exec-3 未取得鎖 因為lock.tryLock(3, 10, TimeUnit.SECONDS),嘗試加鎖,最多等待3秒,上鎖以后10秒后過期自動解鎖 所以等了3秒都等不到,就放棄了
關于“redis實現分布式重入鎖的方法是什么”就介紹到這了,更多相關內容可以搜索億速云以前的文章,希望能夠幫助大家答疑解惑,請多多支持億速云網站!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。