读写锁ReadLock
简单来说就是互斥锁和非互斥锁。多个客户端可以同事加的锁叫读锁,只能有一个客户端加的锁叫写锁。这个理论应该是从数据库中来的,放在这里也是同样的解释。
- 多个客户端同时加读锁,是不会互斥的,多个客户端可以同时加这个读锁,读锁和读锁是不互斥的
- 如果有人加了读锁,此时就不能加写锁,任何人都不能加写锁了,读锁和写锁是互斥的
- 如果有人加了写锁,此时任何人都不能加写锁和读锁了,写锁和写锁也是互斥的
实现
RedissonReadLock是RedissonLock的子类
关注要几块东西,第一个是加读锁的lua脚本的逻辑;第二个是读锁的释放的lua脚本的逻辑;第三个是读锁的wathdog刷新锁key的生存时间的逻辑
RReadWriteLock rwLock = redisson.getReadWriteLock("anyLock");
rwLock.readLock().lock();
rwLock.readLock().unlock();
rwLock.writeLock().lock();
rwLock.writeLock().unlock();
加锁代码
加锁的方法入口都是在RedissonLock类里面,只是在真正加锁的地方用子类来处理。
点进来就可以看到ReadLcok的lua脚本逻辑,下面就来分析一下
假设
客户端A(UUID_01:threadId_01)来加读锁
这几个参数拼接之后是下面的形式
KEYS[1] = anyLock
KEYS[2] = {anyLock}:UUID_01:threadId_01:rwlock_timeout
ARGV[1] = 30000毫秒
ARGV[2] = UUID_01:threadId_01
ARGV[3] = UUID_01:threadId_01:write
前两行的的意思就是要从一个名为anyLock的hash结构中,获取一个key为mode的对应的value值
这是第一个线程第一次进来,肯定是空的,条件成立。
local mode = redis.call(‘hget’, KEYS[1], ‘mode’);
if (mode == false) then
新建一个名为anyLock的hash结构,里面有一个键值对mode:read
redis.call(‘hset’, KEYS[1], ‘mode’, ‘read’);
anyLock的hash结构再来个键值对key=UUID_01:threadId_01 value=1
‘hset’, KEYS[1], ARGV[2], 1
set一个键值对,这两个…的意思是拼接字符串KEYS[2] 原本等于{anyLock}:UUID_01:threadId_01:rwlock_timeout
那KEYS[2] … ‘:1’ = {anyLock}:UUID_01:threadId_01:rwlock_timeout:1
所以这里的键值对就是 key={anyLock}:UUID_01:threadId_01:rwlock_timeout:1
value=1
‘set’, KEYS[2] … ‘:1’, 1
是给上面一个hash数据anyLock,一个普通数据{anyLock}:UUID_01:threadId_01:rwlock_timeout:1
设置过期时间 30000毫秒也就是30s
pexpire
现在加完锁返回为空
看门狗
scheduleExpirationRenewal(),这个方法是通用的RedissonLock里面的实现逻辑,但是其中的延时方法renewExpirationAsync() , RedissonReadLock有个自己的实现
看门狗
lua脚本逻辑
之前我们也分析过普通锁的lua脚本,逻辑是一样的。
简单来说就是只要锁还存在,就不断的延长锁的过期时间避免持有锁的过程中失效。
假如说刚才已经成功加了读锁,我们来看一下这里的参数
KEYS[1] = anyLock
KEYS[2] = {anyLock}
ARGV[1] = 30000毫秒
ARGV[2] = UUID_01:threadId_01
hget anyLock UUID_01:threadId_01,获取一下当前这个线程是否对这个锁加了一个读锁,这里返回的应该是1,此时可以判定是当前这个线程加的读锁
pexpire anyLock 30000,刷新一下anyLock锁key的生存时间为30000毫秒
hlen anyLock = 2 > 1,就是说,如果你的读锁,anyLock hash内部的key-value对超过了1个,这里肯定是成立的,拿到anyLock所有的key
开始进入循环
这里说是,拿到key对应得值,如果是数字的话,进入下面的循环中,如果不是的话不管他
counter = tonumber(redis.call(‘hget’, KEYS[1], key));
if type(counter) == ‘number’ then
此时counter = 1
for i = counter, 1, -1 do
所以
for i = 1, 1, -1 do
加锁的时候我们知道,anyLock里面有个键值对吗,这里的值也是可重入的,同一个线程每重入一次锁,这里的value就+1,
key=UUID_01:threadId_01 value=1
所以延长锁有效期的时候就要根据这个重入的次数来循环延长。
刚才名为anyLock这个hash结构已经延长过了,现在要延长的就是
key={anyLock}:UUID_01:threadId_01:rwlock_timeout
value=1
这个普通类型的键值对,有一次重入,这个时间就要多延长30s。
pexpire’, KEYS[2] … ‘:’ … key … ‘:rwlock_timeout:’ … i, ARGV[1]