Redisson锁机制源码分析( 二 )

关注tryAcquire加锁方法
private Long tryAcquire(long wAItTime, long leaseTime, TimeUnit unit, long threadId) {return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));}【Redisson锁机制源码分析】该方法调用了tryAcquireAsync来实现的,所以我们关注tryAcquireAsync方法,继续跟进 。
关注tryAcquireAsync加锁方法
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {RFuture<Long> ttlRemainingFuture;// 首先判断租约时间是否大于0if (leaseTime > 0) {// 大于零,调用tryLockInnerAsync获取锁ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {// 否则,使用默认的租约时间 追溯下去发现private long lockWatchdogTimeout = 30 * 1000;也就是30s的租约时间ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);}//CompletionStage<Long> f = ttlRemainingFuture.thenApply(ttlRemaining -> {// lock acquired// 结果为空,如果leaseTime大于哦,更新internalLockLeaseTime为指定的超时时间,并且不会启动看门狗(watch dog)if (ttlRemaining == null) {if (leaseTime > 0) {internalLockLeaseTime = unit.toMillis(leaseTime);} else {// 使用定时任务,自动续约(使用看门狗(watch dog))scheduleExpirationRenewal(threadId);}}return ttlRemaining;});return new CompletableFutureWrapper<>(f);}可以看到,加锁最终会调用tryLockInnerAsync进行加锁,而续约会使用scheduleExpirationRenewal进行续约 。
关注tryLockInnerAsync实现真正的加锁逻辑
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));}这里执行了一段lua脚本(整个lua脚本保障原子性),我们将脚本内容复制出来,详细解释一下 。
-- KEYS[1] 加锁的对象(也就是我们传入的的锁名称)-- ARGV[1] 表示锁的过期时间-- ARGV[2]:UUID+当前线程id-- 如果锁不存在 。== 0表示不存在 == 1表示存在if (redis.call('exists', KEYS[1]) == 0) then-- 对我自己的锁执行一个incrby(自增,表示锁的可重入次数)操作redis.call('hincrby', KEYS[1], ARGV[2], 1);-- 对key设置一个过期时间(过期时间就是保证锁的容错性)redis.call('pexpire', KEYS[1], ARGV[1]);-- 返回nil, 相当于null, 表示获取锁成功return nil;end ;-- 继续判断锁名成+UUID+当前线程id是否存在,其实就是判断我自己有没有已经拿到锁(保证锁的可重入性)if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then-- 自己已经持有锁,执行一个incrby(自增,表示锁的可重入次数)操作redis.call('hincrby', KEYS[1], ARGV[2], 1);-- 重新设置过期时间redis.call('pexpire', KEYS[1], ARGV[1]);-- 返回nil, 相当于null, 表示获取锁成功return nil;end ;-- 都不是,表示已经有其他客户端获取到了锁,此时返回key的过期时间,也就是别人释放锁的时间(但其他客户端可能出现续约,存在会等待更久的可能)return redis.call('pttl', KEYS[1]);整个lua脚本保障原子性,从而只会有一个客户端能够获取到锁,这样就保证了锁的互斥性 。
打一个断点看获取到的锁信息

Redisson锁机制源码分析

文章插图
 
hash表中的第一个值表示UUID+线程ID,这二个值表示锁的重入次数,如果锁被多次获取,那么这个值就是大于1 。
关注scheduleExpirationRenewal实现自动续约
protected void scheduleExpirationRenewal(long threadId) {ExpirationEntry entry = new ExpirationEntry();ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);// 不为空表示已经开启了续约操作if (oldEntry != null) {oldEntry.addThreadId(threadId);} else {// 如果没有开启续约操作entry.addThreadId(threadId);try {// 自动续约renewExpiration();} finally {if (Thread.currentThread().isInterrupted()) {cancelExpirationRenewal(threadId);}}}}


推荐阅读