文章目录
类型
使用Redission,lock的机制其实是使用了ttl,一直等ttl为0再get。无论是redission还是redis的setNx,只要是锁,都必须有加锁和释放锁两个动作,二者缺一不可,并且需要把释放锁放到final中,这样可以保证锁一定被释放。 lock和unlock必须搭配使用,缺一不可。
redission中常用的lock就两个,一个是lock一个是trylock。两者都是阻塞加锁, 前者会一直阻塞,相应的该方法不存在返回值,因为无意义;后者可以传入一个等待时间,等待时间内持续阻塞,相应的该方法存在返回值,因为要确认是否成功加了锁。根据业务场景自己判断使用哪种锁,二者其实相似,trylock就是lock的进阶,加了一个等待时间而已。
java
trylock:
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit)
public boolean tryLock(long waitTime, TimeUnit unit)
public boolean tryLock()
lock:
public void lock()
public void lock(long leaseTime, TimeUnit unit)
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly)
waitTime等待/阻塞时间,tryLock若没有则等同于lock;
leaseTime锁持续时间;
unit时间单位。
其实大部分的重载方法都是将参数少/无参的方法加了一些默认值,最后return参数最多的方法。
原理
- 如果没有指定leaseTime(加锁时长),则意味着让看门狗(守护线程,且该守护线程是连接级别,不是线程)管理锁的过期时间,该时间由配置文件控制,默认为30S,然后在过期时间的1/3时刻进行监听一次,线程还在则加时,加时也是走配置文件。
- 如果指定了leaseTime,则意味着自己管理过期时间,看门狗不会介入,即不会自动续期,到了leaseTime之后,如果程序没有执行完毕,依旧会释放锁。此时,其他线程就可以继续对key加锁
- 如果线程A加锁成功,线程B又来加锁,此时线程B会获取到线程A还有多少秒释放锁,然后线程B会通过semaphore信号量来等待一定的时间
- 线程B在等待了一定的时间之后,会重新尝试进行加锁,加锁成功,就返回,如果失败,就重复3步
使用
- 关于lock和trylock的使用,按场景需要进行抉择
- 关于leaseTime是否指定,也就是说要不要使用看门狗控制过期时间,这个很难抉择, 各有利弊,如果可以自己估算业务时间,建议别用看门狗,因为看门狗存在失效或者死锁的可能,但是如果实在估算不出程序执行时间,只能冒险用看门狗了。
看门狗
Redisson中的看门狗(Watchdog)机制用于自动延长锁的持有时间,以防止锁在业务逻辑执行过程中过期。它的工作原理如下:
- 锁续期:当一个线程获取锁后,Redisson会启动一个后台任务(看门狗),定期检查锁的持有时间,并在锁即将过期时自动延长锁的持有时间。
- 自动释放:如果线程正常释放锁,看门狗会停止续期任务。如果线程异常退出,看门狗也会在锁过期后自动释放锁。
然而,虽然看门狗机制设计得很好,但在某些极端情况下,仍然可能出现死锁。以下是一些可能导致死锁的场景:
- 资源竞争:在高并发环境下,多个线程竞争同一个锁,可能会导致某些线程长时间等待锁,从而增加死锁的风险。
- 系统资源耗尽:如果系统资源(如线程池、内存)耗尽,可能会导致看门狗无法正常工作,从而引发死锁。
- 网络问题:如果Redis服务器出现网络问题,导致看门狗无法及时续期锁,可能会导致锁过期,从而引发死锁。
为了避免这些潜在的死锁问题,可以采取以下措施:
- 合理设置锁的超时时间:确保锁的超时时间足够长,以覆盖大部分业务逻辑的执行时间。
- 监控和告警:监控系统的运行状态,及时发现和处理资源耗尽、网络问题等异常情况。
- 优化业务逻辑:尽量减少锁的持有时间,避免长时间占用锁。
总结就是
- Redisson中的看门狗机制通常不会导致死锁,但在高并发、资源耗尽或网络问题等极端情况下,仍然可能出现死锁。
- 通过合理设置锁的超时时间、监控系统状态和优化业务逻辑,可以有效降低死锁的风险。
与setNx比较
在一定程度上redission比setNX更优秀,因为它的所有操作都是通过的lua脚本:
- 先判断key是否存在,如果key不存在,就加锁,并且设置过期时间
- 如果key已经存在,并且当前加锁的key是重入了,那就将key对应的加锁次数加1
- 如果key已存在,并且当前加锁的key和线程和已加锁的不一样,无法重入,那就返回当前key的过期时间