Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下实现的差异和核心的思想。
unfair模式的带超时时间的tryLock(超时时间)
ReentrantLock
这里上来会直接先试下能不能try成功,如果不成功,进入等待并开始竞争等逻辑。
整个锁的核心是通过LockSupport的park方法来实现的,这是调用底层UNSAFE的park方法来实现的。如果在被等待获取的锁释放的时候,该线程会重新被唤醒,然后和其它线程一起竞争,如果没有竞争成功,那么继续park,循环往复,直到获取到锁或者等待超时(这里park的时间是当前要获取锁的线程等待获取锁的剩余时间,当前线程要么因为锁释放被唤醒,要么等到超时)。
这里有个小细节是,如果剩余的等待时间小于spinForTimeoutThreshold这个值,那么就不会再park然后等待唤醒了。这是为了防止park然后唤醒这种线程调度操作,因为剩下的等待时间已经不多,即使一直自旋也不会消耗太多的CPU性能,反而比频繁挂起释放要节省性能,这是一个折衷处理。
RedissonLock
RedissonLock没有LockSupport这种底层级别的工具来帮忙,因为它是分布式的,所以它也需要借助这样一个东西,来实现类似的功能。
我们一般在自己用Redis实现分布式锁的时候,经常设计的操作是轮询Redis去查询该锁的状态,轮询之间会设置一个休眠时间,休眠时间越短,当锁释放的时候响应的就越及时,但是对Redis的压力就越大,因为你单位时间内轮询Redis的次数会增加,所以这是一个痛点。那么如果我们用通知来代替轮询,是不是就可以仿照ReentrantLock那样,通过唤醒操作(借助通知)来唤醒本地的sleep操作,这样就不必定时轮询检查状态了。
而这个功能就要利用Redis的订阅功能,下面看代码:
这里和ReentrantLock一样,先try一下,如果获得了锁就返回,如果没有获得,看下是否已经超过等待获取锁设置的超时时间,如果已经超时,获取锁失败,否则,就要进入等待竞争的过程。
竞争的过程中,第一个操作是subcribe,即上面我们提到的订阅功能。订阅的超时时间即当前要获取锁的线程的剩余等待时间,如果在这个时间范围内没有响应,订阅失败。
订阅的相关操作可以参考:https://my.oschina.net/u/2313177/blog/1925237。
“订阅监听Redis消息,并且创建RedissonLockEntry,其中RedissonLockEntry中比较关键的是一个Semaphore属性对象,用来控制本地的锁请求的信号量同步,返回的是netty框架的Future实现”。
如果这个await成功了,代表在等待锁的超时时间之内锁就被释放了,接下来要进入竞态部分了。
这里我理解有两层维度,一层是应用维度,不同的虚拟机之间通过订阅方式竞争锁,某一台业务服务器的java虚拟机会最终成功订阅到这个锁。二层是虚拟机内的线程维度,这里机器内的竞争通过一个共享锁来减少对Redis的压力,因为当前虚拟机的订阅下面挂着多个竞争该锁的不同线程,这些线程中也只有一个会成功获得共享锁,继而再去竞争真正的锁。没有获得共享锁的线程睡眠一段时间,或者被唤醒继续获取锁,或者超时,获取锁失败,而只有获得共享锁成功的线程才有机会和其它虚拟机中同样获取共享锁的线程共同竞争真正的锁。
所以同虚拟机内要竞争真正锁的所有线程先竞争一个共享锁(Semaphore),成功的线程才获取机会和其它虚拟机内同样获取共享锁(Semaphore)的线程竞争真正的锁——跨虚拟机的锁竞争,即分布式锁竞争,这里的关键是共享锁降低了静态,同时又利用订阅机制(通知机制)解决了睡眠时间无法合理设置的问题。
其实redisson实现分布式锁原理 - min.jiang - 博客园文章也说到了这个问题:
所以,如果订阅成功,说明当前机器在虚拟机层面首先抢占到了资源,然后再在当前虚拟机内进行不同线程间的竞争。
像上面说的那样,这里借助信号量来替代了ReentrantLock中的LockSupport的park功能,“通过信号量(共享锁)阻塞,等待解锁消息(这一点设计的非常精妙:减少了其他分布式节点的等待或者空转等无效锁申请的操作,整体提高了性能)。如果剩余时间(ttl)小于wait time ,就在 ttl 时间内,从Entry的信号量获取一个许可(除非被中断或者一直没有可用的许可)。否则就在wait time时间范围内等待”。
这里的共享锁和上面subscribe的是同一把锁。如果本虚拟机的某个线程获取锁之后,当它释放锁的时候,会发布一条取消订阅的消息(这个后面具体会说),这样其它虚拟机才能有平等的机会再次和本虚拟机的其他线程竞争锁,而不是一直在本虚拟机的范围内进行竞争,那样其他虚拟机就会一直处于饥饿状态。
那么什么时候这个订阅的消息会解除当前线程的休眠操作呢?和subscribe对应的当然就是publish了,当执获得锁的线程进行解锁操作的时候(解锁后面会单独说),在lua中会执行‘publish’操作,而publish的消息类型是LockPubSub.unlockMessage,这个消息会触发订阅消息的共享锁(Semaphore)的唤醒操作,然后发起对该锁的新一轮竞争。
而最后无论获取锁成功与否,都要解除当前线程对该锁的订阅消息。
关于等待时间(Semaphore的休眠时间),上面ReentrantLock的park的时间是当前线程获取锁的等待剩余时间。这里本地等待锁的睡眠时间略有不同,使用的是Redis中锁的生命周期剩余时间,当然在这个睡眠过程中,可能会因为锁释放而唤醒,因为有对当前锁的订阅操作。
Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock相关推荐
- Redisson(2-3)分布式锁实现对比 VS Java的ReentrantLock的FairLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下公平锁(Fair)实现的差异和核心的思想. 公平锁存在的意义是为了保证绝对的公平,但是有其弊端,这个在网上有很多相关的 ...
- Redisson(2-1)分布式锁实现对比 VS Java的ReentrantLock之tryLock
Redisson实现了一整套JDK中ReentrantLock的功能,这里对比一下实现的差异和核心的思想. unfair模式的tryLock ReentrantLock ①判断当前的state是否是0 ...
- redis分布式锁与zk分布式锁的对比
在分布式环境下,传统的jvm级别的锁会失效,那么分布式锁就是非常有必要的一个技术,一般我们可以通过redis,zk等技术来实现我们的分布式锁 redis实现分布式锁: 原理:我们都知道redis的处理 ...
- Redisson实现Redis分布式锁的N种姿势
点击蓝色"程序猿DD"关注我哟 来源:阿飞的博客 前几天发的一篇文章<Redlock:Redis分布式锁最牛逼的实现>,引起了一些同学的讨论,也有一些同学提出了一些疑问 ...
- springboot整合redisson实现多种分布式锁
Redisson概述 Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid).它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式 ...
- 若依集成redisson实现redis分布式锁
Redisson是Redis服务器上的分布式可伸缩Java数据结构----驻内存数据网格(In-Memory Data Grid,IMDG).底层使用netty框架,并提供了与java对象相对应的分布 ...
- RedisSon高并发分布式锁实战RedisSon源码解读
Redis高并发分布式锁实战 1.分布式场景下的synchronized失效的问题–用redis实现分布式锁 synchronized是通过monitor实现的jvm级别的锁,如果是分布式系统,跑在不 ...
- Mysql学习总结(83)——常用的几种分布式锁:ZK分布式锁、Redis分布式锁、数据库分布式锁、基于JDK的分布式锁方案对比总结
一.基于数据库实现分布式锁 1.1.悲观锁 利用select - where - for update 排他锁.注意: 其他附加功能与实现一基本一致,这里需要注意的是"where name= ...
- java分布式_分布式锁的四种JAVA实现方式
前言 作为这一段时间学习分布式锁的总结,本文总结了四种Java分布式锁的实现方式,简单编写了代码,进行模拟实现.相关代码存放在我的github仓库. 为什么要用锁 系统内,有多个消费者,需要对同一共享 ...
最新文章
- Android开发精要1--Android系统架构
- python笔记之函数参数(缺省参数,命名参数,不定长参数)
- Mac os下android studio模拟器无法联网解决方法
- MySQL -- SQL 语句
- 推荐:26种NLP练手项目(代码+数据)
- 拥抱云原生,Fluid结合JindoFS :阿里云OSS加速利器
- linux vi刷新页面,vim的神级配置 - bubifengyun的个人页面 - OSCHINA - 中文开源技术交流社区...
- python显示小数点后几位数_Python编程从入门到实践-连载1(变量和简单数据类型)...
- SAP License:SAP 清帐介绍
- VLC设置串流的TTL值
- ocv特性_SOC-OCV曲线是否始终值得信赖的吗?
- GDB调试的一系列博客
- winload.exe数字签名问题详解
- R语言-批量转换变量类型为因子型
- 什么是cidaemon.exe进程
- u盘chk文件恢复图文教程
- 数据库复杂查询,左联右联 聚合 计数 时间查询等,持续更新
- selenium打开chrome浏览器闪退(进程结束后关闭浏览器)解决方法
- 华为云计算IE面试笔记-eBackup有哪几种备份组网方式,各备份组网方式主要的应用场景及备份流程?
- Numpy.array()详解 、np.array与np.asarray辨析、 np.array和np.ndarry的区别