下面四种都是比较极端情况下可能出现的问题,其实前面实现的已经很够用了

Redission



Redission可重入锁的原理
之前的方法无法实现可重入锁

可重入锁的实现其实就是换成Hash类型,然后添加一个申请次数,线程号是自己就+1,释放锁就-1.最后一次执行删除。但是比较复杂的是Hash中没有setnx,所以要先判断再申请,把流程拆开



完整流程,其实可以用Lua脚本实现




解决不可重试问题

最核心的一句,这一步是获取锁的剩余有效期:

获得剩余时间以后会判断当前还有没有剩余等待时间,没有就返回失败,有就继续判断

释放锁的时候有一行发布一个释放消息

继续执行的代码会执行一个订阅操作,订阅的就是释放锁的消息。然后紧跟判断,如果在最大剩余等待时间以内订阅消息一直没有出现,会取消订阅并返回false。


如果依然有剩余等待时间,就会重新提交一次请求锁操作



解决超市释放的安全问题
如果TTL快到期了,但是程序还没执行完,此时实现了一个TTL续约的机制

这里的putIfAbsent是如果 key 不存在,就把 key-value 放进去;如果 key 已经存在,不覆盖,直接返回旧值。

下面是具体的续约机制


newTimeout是一个延时的任务,会在参数的时间到期以后执行。执行完成以后外面会调用自己,此时锁的有效时间就会无限延期

执行刷新有效期

同样怎么让他不在刷新呢,其实就是在解锁过程中取消订阅执行。

取消定时任务

不可重试+超时释放完整流程


主从一致性导致的锁失效问题
最开始有主节点和从节点,主节点负责写,从节点负责读

但是出现了问题,主节点宕机了,此时会从从节点中选择一个作为主节点。但是此时java应用和之前的主节点的锁失效,此时新的主节点相当于没有锁,所有的线程都可以去申请加锁

一种解决方法就是放弃主从,全部加锁

但是这样就没有主从了,因此我们给多个主节点都加上从节点。对多个主节点加锁,全部加上锁才算是获取成功,其他主节点的锁没有释放当然不可以被获取。

如果一个主节点宕机,此时还有其他主节点存在,此时的锁还没有被释放,因此其他线程不能加锁。因为此时是对多个主节点加锁,全部加上锁才算是获取成功,其他主节点的锁没有释放当然不可以被获取。

redis分布式联锁的实现
首先实现了多个节点,也就是建立了多个redis数据库,此时我们当然要给每一个都进行配置

配置的时候也写上多个redis

创建联锁,这里会将多个锁加入一个List中,加锁的时候会把所有的锁全都进行加锁,如果全部成功才算是获取锁成功。然后具体的加锁解锁流程和普通的lock一样。
注意,联锁中的每一个锁都是可重入锁

