首页 > 数据库 >一篇文章弄懂Redission可重入、重试锁以及MultiLock原理

一篇文章弄懂Redission可重入、重试锁以及MultiLock原理

时间:2024-10-19 16:48:37浏览次数:3  
标签:释放 加锁 Redis MultiLock 重试 Redission 线程 Redisson

Redisson的可重入锁(Reentrant Lock)是基于Redis实现的分布式锁,用于在分布式系统中提供线程安全的锁机制。它允许同一个线程在不释放锁的情况下多次获得锁,并在所有锁操作完成后,锁才真正被释放。下面我们来详细解析Redisson可重入锁的原理。

基本原理

可重入锁的核心思想是,同一线程可以多次获取同一个锁,且只有当所有锁释放操作都完成后,锁才会真正释放

锁的可重入特性

Redisson通过维护一个计数器来实现锁的可重入特性。

  • 当同一个线程第一次获取锁时,Redis会记录下这个线程的<threadId>,并将锁的持有次数设置为1。
  • 如果这个线程再次请求锁(即可重入操作),Redisson会检测到当前持有锁的<threadId>与当前线程相同,则不会重新设置锁,而是简单地增加计数器,表示这个线程再次持有了锁。
  • 每次释放锁时,Redisson会减少计数器,只有当计数器减为0时,锁才会真正释放。
锁的释放操作

锁的释放操作需要确保只有持有锁的线程才能释放锁。Redisson会在释放锁时检查Redis中存储的<threadId>与当前线程的ID是否匹配,确保线程安全。

释放锁的Lua脚本逻辑:

-- 如果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

逻辑:如果当前线程ID与Redis中保存的线程ID相同,则检查计数器的值。如果计数器减为0,删除锁;否则,只减少计数器值。

关键机制

锁的自动过期

为了防止死锁,Redisson为锁设置了过期时间。如果持有锁的线程因为意外情况(如崩溃、网络分区等)未能及时释放锁,Redis会在锁的过期时间后自动释放,防止锁长时间占用资源。

Watchdog机制(看门狗)

Redisson提供了Watchdog机制,用于动态延长锁的有效期。默认情况下,Redisson的锁在过期之前,会通过看门狗定期检查线程的状态:

  • 如果线程仍然在持有锁(即线程还在运行且没有释放锁),看门狗会自动延长锁的过期时间,防止锁过早释放。
  • 默认的看门狗延时是30秒,Redisson会在锁的持有期间不断续期。
Lua脚本保证原子性

Redisson使用了Redis的Lua脚本来执行锁的加锁、释放等操作。Lua脚本在Redis中具有原子性,这意味着即使在分布式环境下,这些操作也不会受到并发影响,从而保证了锁的安全性和一致性。

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();  // 释放锁
    }
}

MultiLock

MultiLock 原理解析
  • 多锁合并MultiLock 包含多个 RLock 实例。它会尝试同时获取这些锁,只有当所有锁都成功获取时,才算加锁成功。

  • 加锁过程

    • 当调用 lock() 方法时,MultiLock 会依次尝试获取多个 RLock(即多个 Redis 实例上的锁),并且每个锁都有独立的超时时间。
    • 如果某一个锁获取失败,MultiLock回滚已经获取的锁,确保加锁操作的原子性——要么全部加锁成功,要么加锁失败,并释放已获得的锁。
  • 解锁过程

    • 当所有锁都被成功获取并且业务处理完成时,unlock() 方法会逐个释放这些锁。解锁时同样遵循先加锁的顺序。
    • 如果某个锁释放失败,系统会继续尝试释放其他锁,确保整体资源的一致性。
  • 可重入性

    • MultiLock可重入的,如果一个线程已经持有某些资源的锁,它可以再次申请这些锁,而不会阻塞。这是通过 Redis 的可重入锁机制实现的,类似于单个 RLock 的可重入锁。
  • 超时和看门狗机制

    • 和普通的 Redisson 锁一样,MultiLock 也可以配置超时时间,并通过 Redisson 的看门狗机制自动续期,以防止锁在任务尚未完成时被意外释放。
具体实现过程
  1. 加锁请求:当客户端尝试加锁时,MultiLock 会向多个 Redis 实例发送加锁请求。每个加锁请求带有一个唯一标识符(通常为线程ID),并设置一个过期时间来防止死锁。

  2. 全成功条件:只有当所有的加锁请求都成功(即多个 RLock 都被锁定),MultiLock 才算是成功加锁。如果任何一个锁失败,则需要释放已经成功的锁。

  3. 释放锁:释放锁时,同样会发送多个解锁请求,只有当所有锁都被释放时,才算成功解锁。

标签:释放,加锁,Redis,MultiLock,重试,Redission,线程,Redisson
From: https://blog.csdn.net/weixin_53891720/article/details/142940284

相关文章