什麼是可重入鎖?怎麼實現重入鎖?以下這篇文章就來帶大家深入聊聊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(); } } }
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也是可重入鎖。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(); } }
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次--------
Redisson是Redis官方推薦的Java版的Redis客戶端。
基於Java實用工具包中常用接口,為使用者提供了一系列具有分散式特性的常用工具類別。在網路通訊上是基於NIO的Netty框架,保證網路通訊的高效能。
在分散式鎖定的功能上,它提供了一系列的分散式鎖定;如:非公平鎖定(unFair Lock)可重入鎖定(Reentrant Lock)
公平鎖定(Fair Lock)
讀取寫入鎖定(ReadWriteLock)
連鎖(MultiLock)紅鎖(RedLock)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()); } } }
最佳实战:
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解锁退出
通过测试结果:
上面介绍了分布式重入锁的相关知识,证明了Redisson工具能实现了可重入锁的功能。其实Redisson工具包中还包含了读写锁(ReadWriteLock)和 红锁(RedLock)等相关功能,我们下篇文章再详细研究。
更多编程相关知识,请访问:编程入门!!
以上是什麼是可重入鎖?詳解redis實現分散式重入鎖的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!