这篇文章主要为大家分析了redis实现分布式重入锁的方法是什么的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“redis实现分布式重入锁的方法是什么”的知识吧。

什么是不可重入锁?

即若当前线程执行某个方法已经获取了该锁,那么在方法中尝试再次获取锁时,就会获取不到而阻塞。

什么是可重入锁?

可重入锁,也叫做递归锁,指的是在同一线程内,外层函数获得锁之后,内层递归函数仍然可以获取到该锁。就是同一个线程再次进入同样代码时,可以再次拿到该锁。

可重入锁作用?

防止在同一线程中多次获取锁而导致死锁发生。

注:在java的编程中synchronized 和 ReentrantLock都是可重入锁。

基于synchronized的可重入锁

步骤1:双重加锁逻辑

publicclassSynchronizedDemo{//模拟库存100intcount=100;publicsynchronizedvoidoperation(){log.info("第一层锁:减库存");//模拟减库存count--;add();log.info("下订单结束库存剩余:{}",count);}privatesynchronizedvoidadd(){log.info("第二层锁:插入订单");try{Thread.sleep(1000*10);}catch(InterruptedExceptione){e.printStackTrace();}}}

步骤2:加个测试类

publicstaticvoidmain(String[]args){SynchronizedDemosynchronizedDemo=newSynchronizedDemo();for(inti=0;i<3;i++){intfinalI=i;newThread(()->{log.info("-------用户{}开始下单--------",finalI);synchronizedDemo.operation();}).start();}}

步骤3:测试

20:44:04.013[Thread-2]INFOcom.agan.redis.controller.SynchronizedController--------用户2开始下单--------20:44:04.013[Thread-1]INFOcom.agan.redis.controller.SynchronizedController--------用户1开始下单--------20:44:04.013[Thread-0]INFOcom.agan.redis.controller.SynchronizedController--------用户0开始下单--------20:44:04.016[Thread-2]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第一层锁:减库存20:44:04.016[Thread-2]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第二层锁:插入订单20:44:14.017[Thread-2]INFOcom.agan.redis.Reentrant.SynchronizedDemo-下订单结束库存剩余:9920:44:14.017[Thread-0]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第一层锁:减库存20:44:14.017[Thread-0]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第二层锁:插入订单20:44:24.017[Thread-0]INFOcom.agan.redis.Reentrant.SynchronizedDemo-下订单结束库存剩余:9820:44:24.017[Thread-1]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第一层锁:减库存20:44:24.017[Thread-1]INFOcom.agan.redis.Reentrant.SynchronizedDemo-第二层锁:插入订单20:44:34.017[Thread-1]INFOcom.agan.redis.Reentrant.SynchronizedDemo-下订单结束库存剩余:97

由于synchronized关键字修饰的是方法,所有加锁为实例对象:synchronizedDemo

运行结果可以看出减库存和插入订单都是每个线程都完整运行两个方法完毕,才能释放锁,其他线程才能拿锁,即是一个线程多次可以拿到同一个锁,可重入。所以synchronized也是可重入锁。

基于ReentrantLock的可重入锁

ReentrantLock,是一个可重入且独占式的锁,是一种递归无阻塞的同步锁。和synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。

步骤1:双重加锁逻辑

publicclassReentrantLockDemo{privateLocklock=newReentrantLock();publicvoiddoSomething(intn){try{//进入递归第一件事:加锁lock.lock();log.info("--------递归{}次--------",n);if(n<=2){try{Thread.sleep(1000*2);}catch(InterruptedExceptione){e.printStackTrace();}this.doSomething(++n);}else{return;}}finally{lock.unlock();}}}

步骤2:加个测试类

publicstaticvoidmain(String[]args){ReentrantLockDemoreentrantLockDemo=newReentrantLockDemo();for(inti=0;i<3;i++){intfinalI=i;newThread(()->{log.info("-------用户{}开始下单--------",finalI);reentrantLockDemo.doSomething(1);}).start();}}

步骤3:测试

20:55:23.533[Thread-1]INFOcom.agan.redis.controller.ReentrantController--------用户1开始下单--------20:55:23.533[Thread-2]INFOcom.agan.redis.controller.ReentrantController--------用户2开始下单--------20:55:23.533[Thread-0]INFOcom.agan.redis.controller.ReentrantController--------用户0开始下单--------20:55:23.536[Thread-1]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归1次--------20:55:25.537[Thread-1]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归2次--------20:55:27.538[Thread-1]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归3次--------20:55:27.538[Thread-2]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归1次--------20:55:29.538[Thread-2]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归2次--------20:55:31.539[Thread-2]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归3次--------20:55:31.539[Thread-0]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归1次--------20:55:33.539[Thread-0]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归2次--------20:55:35.540[Thread-0]INFOcom.agan.redis.Reentrant.ReentrantLockDemo---------递归3次--------

运行结果可以看出,每个线程都可以多次加锁解锁的,ReentrantLock是可重入的。

redis如何实现分布式重入锁?

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重入锁测试类

publicclassRedisController{@AutowiredRedissonClientredissonClient;@GetMapping(value="/lock")publicvoidget(Stringkey)throwsInterruptedException{this.getLock(key,1);}privatevoidgetLock(Stringkey,intn)throwsInterruptedException{//模拟递归,3次递归后退出if(n>3){return;}//步骤1:获取一个分布式可重入锁RLock//分布式可重入锁RLock:实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁。RLocklock=redissonClient.getLock(key);//步骤2:尝试拿锁//1.默认的拿锁//lock.tryLock();//2.支持过期解锁功能,10秒钟以后过期自动解锁,无需调用unlock方法手动解锁//lock.tryLock(10,TimeUnit.SECONDS);//3.尝试加锁,最多等待3秒,上锁以后10秒后过期自动解锁//lock.tryLock(3,10,TimeUnit.SECONDS);booleanbs=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(Exceptione){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实现分布式重入锁的方法是什么”就介绍到这了,更多相关内容可以搜索亿速云以前的文章,希望能够帮助大家答疑解惑,请多多支持亿速云网站!