我们先配置下RedissonClient并注册为Bean,指定锁的名称,通过 getLock,拿到redissonLock。
因为我们上面没有传参数,调用这个无参的方法,其他方法分别是未设置等待时间(没有设置重试功能,waitTime默认为-1),未设置锁的过期时间(开启看门狗机制,leaseTime默认为-1)
注意这些方法返回值都是RFuture<Boolean>
重试机制:
对于可重试功能,表明在调用tryLock方法时,给了第一个参数waitTime,即上面的第三,四个方法。这两个方法区别就是有无看门狗机制,下面会讲。 我们先进入第四个方法的调用。
会调用tryAcquireAsync的方法,通过它的回调结果去进行相应的操作。
先判断是否设置了锁的过期时间,即判断是否开启了看门狗机制,这里我们进入第一个if分支(未开启看门狗机制)
这个方法先进行统一判定,如果有看门狗机制则读取锁的过期时间,没有则直接转换成毫秒,然后进行一段lua脚本。逻辑就是锁的重入机制嘛,当前是否进来过,没进来的话,则通过hincrby方法创建hash结构,同时设置字段为1,并刷新过期时间。进来过的话,则直接给当前线程id的次数+1,同时刷新过期时间。注意,这里成功都是返回nil,失败返回key的过期时间。后面会根据lua脚本的返回值去做进一步判断。
回到这张图,上面三张图,我们根据lua脚本的返回值,如果ttl==null,说明执行成功了,返回true,如果执行失败(可能是没有拿到锁嘛),就会计算锁的等待时间,是否还需要继续等待,如果超过了等待时间waitTime,则直接返回false,否则开启订阅subscribe(目标线程id)
如果订阅的那个线程释放了锁,它就会publish给所有订阅了的线程一个信号(后面会讲),那我们就会重新去竞争这把锁,进入onComplete回调方法,ex是异常信息,AtomicReference是一个超时任务(定时任务,即指定时间内没有完成,则会触发的一个任务)
这个超时任务的创建是在!subscribeFuture.isDone()这个if分支触发的,即如果订阅失败,则它会去创建这个定时任务,并存放在futureRef变量中。而futureRef.get()!=null if分支是为了判断当前是否有定时任务,如果能进入这里,前提一定是完成了订阅,同时没有报错的前提下,这个时候再把定时任务给取消掉。当然,订阅成功不代表一定能抢到锁,会先进入tryLockAsync方法
通过subscribeFuture获取到entry,当锁发生变化时,latch 会被释放,从而允许线程继续执行。
如果 latch 可以获取,说明锁的状态发生了变化,可以继续尝试获取锁。当然,如果没拿到信号量,就开启监听器,以及一个定时任务,逻辑和上面那个定时任务差不多,都是去等别人发消息。
当信号量释放的时候,就会触发这个监听器,然后尝试去获得锁。
避免超时释放:
上面也有讲,在tryLock的时候,不设置锁的leaseTime(过期时间)scheduleExpirationRenewal
同时这里还有个很重要的map
可重入:上面有讲,其实就是在获取锁的时候,多了一个判定, 是否是当前线程id拿到的锁,是的话,hash数据结构中的value字段次数+1,同样的,如果释放锁,会先去给value -1,然后也会通过当前线程id去判断次数此时是否>0,如果count>0,说明还不能删除这个key嘛,如果count = 0 ,则del这个key。