关于 RedissonWriteLock 释放锁的源码,我们这里也是不再做分析了,因为 RedissonWriteLock 也是基于 RedissonLock 做的扩展,所以释放锁的源码也是和 RedissonLock 保持一致的,我们这里只需要分析 lua 脚本是如何执行即可。
分析前,我们定一下加锁的key:
RReadWriteLock readWriteLock = client.getReadWriteLock("myLock");
RedissonWriteLock#unlockInnerAsync:
@Override
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local mode = redis.call('hget', KEYS[1], 'mode'); " +
"if (mode == false) then " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end;" +
"if (mode == 'write') then " +
"local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); " +
"if (lockExists == 0) then " +
"return nil;" +
"else " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call('hdel', KEYS[1], ARGV[3]); " +
"if (redis.call('hlen', KEYS[1]) == 1) then " +
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"else " +
// has unlocked read-locks
"redis.call('hset', KEYS[1], 'mode', 'read'); " +
"end; " +
"return 1; "+
"end; " +
"end; " +
"end; "
+ "return nil;",
Arrays.<Object>asList(getName(), getChannelName()),
LockPubSub.READ_UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
我们可以看到,这读锁释放锁的lua脚本不算长,一步一步分析就可以了。
Arrays.
KEYS:["myLock","redisson_rwlock:{myLock}"]
LockPubSub.UNLOCK_MESSAGE, getLockName(threadId):
ARGVS:[0L,"UUID:threadId:write"]
场景:
lua脚本:
"local mode = redis.call('hget', KEYS[1], 'mode'); " +
"if (mode == false) then " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end; "
分析:
1 . 利用 hget 命令获取读写锁的模式
hget myLock mode
2 . 如果锁模式为空,往读写锁对应的channel发送释放锁的消息,然后返回1,lua脚本执行完毕
publish redisson_rwlock:{myLock} 0
场景:
lua脚本:
"local lockExists = redis.call('hexists', KEYS[1], ARGV[3]); " +
"if (lockExists == 0) then " +
"return nil;"
"else"
分析:
1 . 利用 hexists 命令判断当前线程是否持有锁
hexists myLock UUID:threadId:write
2 . 如果不存在直接返回null,表示释放锁失败
场景:
lua脚本:
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else "
分析:
1 . 利用 hincrby 命令扣减持有锁数量
hincrby myLock UUID:threadId:write -1
2 . 扣减持有锁数量后,剩余持有锁数量大于0,利用 pexpire 命令重新刷新锁过期时间
pexpire myLock 30000
场景:
lua脚本:
"redis.call('hdel', KEYS[1], ARGV[3]); " +
"if (redis.call('hlen', KEYS[1]) == 1) then " +
"redis.call('del', KEYS[1]); " +
"redis.call('publish', KEYS[2], ARGV[1]); " +
"else " +
// has unlocked read-locks
"redis.call('hset', KEYS[1], 'mode', 'read'); " +
"end; " +
"return 1; "
分析:
1 . 利用 del 命令删除写锁记录
hdel myLock UUID:threadId:write
2 . 删除写锁记录后,利用 hlen 判断锁map里还存在多少个key
hlen myLock
del myLock
public redisson_rwlock:{myLock} 0
如果 key 数量大于1,证明当前线程还持有读锁,利用 hset 命令将锁模式设置为读锁
hset myLock mode read
3 . 最后返回1,释放锁成功,终止lua脚本执行
到此,关于 Redisson 读写锁的原理分析都基本分析完了,包括读锁、写锁的加锁和释放锁原理。
Copyright© 2013-2020
All Rights Reserved 京ICP备2023019179号-8