Redisson的可重入锁(Reentrant Lock)是基于Redis实现的分布式锁。
用于在分布式系统中提供线程安全的锁机制。它允许同一个线程在不释放锁的情况下多次获得锁,并在所有锁操作完成后,锁才真正被释放。
1.基本原理
可重入锁的核心思想是,同一线程可以多次获取同一个锁,且只有当所有锁释放操作都完成后,锁才会真正释放。
锁的可重入特性
Redisson通过维护一个计数器来实现锁的可重入特性。
- 当同一个线程第一次获取锁时,Redis会记录下这个线程的,并将锁的持有次数设置为1。
- 如果这个线程再次请求锁(即可重入操作),Redisson会检测到当前持有锁的与当前线程相同,则不会重新设置锁,而是简单地增加计数器,表示这个线程再次持有了锁。
- 每次释放锁时,Redisson会减少计数器,只有当计数器减为0时,锁才会真正释放。
锁的释放操作
锁的释放操作需要确保只有持有锁的线程才能释放锁。Redisson会在释放锁时检查Redis中存储的与当前线程的ID是否匹配,确保线程安全。
释放锁的Lua脚本逻辑:如果当前线程ID与Redis中保存的线程ID相同,则检查计数器的值。如果计数器减为0,删除锁;否则,只减少计数器值。
-- 如果Redis中存储的锁的持有者与当前线程的标识符相等(ARGV[1]),则进行解锁操作
if redis.call('get', KEYS[1]) == ARGV[1] then
-- 尝试递减计数器(KEYS[2]为计数器的Key),表示释放一次锁
if redis.call('decr', KEYS[2]) == 0 then
-- 如果计数器减为0,说明锁完全释放,删除锁的Key
return redis.call('del', KEYS[1])
else
-- 如果计数器未减为0,返回0表示锁未完全释放,暂时不删除锁
return 0
end
else
-- 如果当前线程标识不匹配,说明不是该线程持有的锁,返回0表示无法解锁
return 0
end
2.关键机制
- 锁的自动过期
为了防止死锁,Redisson为锁设置了过期时间。
如果持有锁的线程因为意外情况(如崩溃、网络分区等)未能及时释放锁,Redis会在锁的过期时间后自动释放,防止锁长时间占用资源。
- Watchdog机制(看门狗)
Redisson提供了Watchdog机制,用于动态延长锁的有效期。默认情况下,Redisson的锁在过期之前,会通过看门狗定期检查线程的状态:
如果线程仍然在持有锁(即线程还在运行且没有释放锁),看门狗会自动延长锁的过期时间,防止锁过早释放。
默认的看门狗延时是30秒,Redisson会在锁的持有期间不断续期。
- Lua脚本保证原子性
Redisson使用了Redis的Lua脚本来执行锁的加锁、释放等操作。
Lua脚本在Redis中具有原子性,这意味着即使在分布式环境下,这些操作也不会受到并发影响,从而保证了锁的安全性和一致性。
3.Redisson 可重入锁的代码示例
RLock lock = redissonClient.getLock("myLock");
try {
// 尝试加锁,最多等待10秒,上锁以后20秒自动解锁
boolean isLocked = lock.tryLock(10, 20, TimeUnit.SECONDS);
if (isLocked) {
// 执行业务逻辑
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock(); // 释放锁
}
}
4.MultiLock
原理解析
-
多锁合并:MultiLock 包含多个 RLock 实例。它会尝试同时获取这些锁,只有当所有锁都成功获取时,才算加锁成功。
-
加锁过程:
-
- 当调用 lock() 方法时,MultiLock 会依次尝试获取多个 RLock(即多个 Redis 实例上的锁),并且每个锁都有独立的超时时间。
-
- 如果某一个锁获取失败,MultiLock 会回滚已经获取的锁,确保加锁操作的原子性——要么全部加锁成功,要么加锁失败,并释放已获得的锁。
-
解锁过程:
-
- 当所有锁都被成功获取并且业务处理完成时,unlock() 方法会逐个释放这些锁。解锁时同样遵循先加锁的顺序。
-
- 如果某个锁释放失败,系统会继续尝试释放其他锁,确保整体资源的一致性。
可重入性:
- MultiLock 是可重入的,如果一个线程已经持有某些资源的锁,它可以再次申请这些锁,而不会阻塞。这是通过 Redis 的可重入锁机制实现的,类似于单个 RLock 的可重入锁。
超时和看门狗机制:
- 和普通的 Redisson 锁一样,MultiLock 也可以配置超时时间,并通过 Redisson 的看门狗机制自动续期,以防止锁在任务尚未完成时被意外释放。
具体实现过程
-
加锁请求:当客户端尝试加锁时,MultiLock 会向多个 Redis 实例发送加锁请求。每个加锁请求带有一个唯一标识符(通常为线程ID),并设置一个过期时间来防止死锁。
-
全成功条件:只有当所有的加锁请求都成功(即多个 RLock 都被锁定),MultiLock 才算是成功加锁。如果任何一个锁失败,则需要释放已经成功的锁。
-
释放锁:释放锁时,同样会发送多个解锁请求,只有当所有锁都被释放时,才算成功解锁。