跳转至

Redisson

image-20250115231112111

API

tryLock() 中:

  • 第一个参数表示返回 false 前的最大等待时间(在这段时间内会重试)
  • 第二个参数表示锁的 TTL
  • 第三个参数即时间单位l

无参数锁则直接返回是否获取成功

@Resource
private RedissonClient redissonClient;

@Test
void testRedisson() throws InterruptedException {
    RLock lock = redissonClient.getLock("name");
    boolean gotLock = lock.tryLock(1, 10, TimeUnit.SECONDS);

    if(gotLock()){
        try {
            /* 执行业务 */
        } finally {
            lock.unlock();
        }
    }
}

可重入

原理

重入:在一个线程中多次尝试获取同一个锁

  • 在之前基于 setnx 实现的锁是不可重入的
    • key 表示锁的名称
    • value 表示持锁线程

Redisson 的可重入使用 hash 结构:

image-20250115213211481

image-20250115213451033

使用 lua 脚本来实现原子性

实现

加锁脚本

local key = KEYS[1];
local threadId = ARGV[1];
local releaseTime = ARGV[2];

-- 锁不存在
if(redis.call('exist', key) == 0) then
    -- 设置 hash 条目
    redis.call('hset', key, threadId, 1);
    -- 设置过期时间
    redis.call('expire', key, releaseTime);
    return 1;
end;

-- 锁已经存在,判断持锁线程
if(redis.call('hexist', key, threadId) == 1) then
    redis.call('hincrby', key, threadId, '1');
    -- 重入时重置过期时间
    redis.call('expire', key, releaseTime);
    return 1;
end;
return 0;

解锁脚本

-- 所不是此线程的
if(redis.call('hexist', key, releaseTime) == 0) then
    return nil;
end

local cnt = redis.call('hincrby', key, threadId, -1);
if(cnt > 0) then
    redis.call('expire', key, releaseTime);
    return nil;
end
else
    redis.call('del', key);
    return nil;
end;

重试与续期

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit){}
  1. 设置了 leaseTime
    • Redisson 不会启动 watchdog 定时任务。
    • 锁会在 leaseTime 时间后自动释放,即使客户端仍持有锁。
  2. 未设置 leaseTime
    • Redisson 会启用 watchdog 机制,定期续期锁。
    • 锁不会因超时自动释放,除非客户端主动释放或崩溃。

image-20250115223127113

如果使用 lock() 且不设置 waitTime,则会一直阻塞等待锁;tryLock() 不设置 waitTime 则不会等待锁,直接返回获取结果

主从一致

主从模式

  • Redis 主节点负责读写
  • 从节点只负责读

multi-lock 模式

只有在每个节点都成功获取锁才行

image-20250115232103732

总结

  1. 可重入:利用 redis 的 hash 结构,field 为线程的唯一标识,value 为冲入次数
  2. 可重试:使用信号量和发布订阅机制,在 subscribe 后阻塞等待(不占用 cpu),直到被通知唤醒
  3. 超时续期:使用 watchdog 机制,每隔 releaseTime / 3 毫秒重置超时时间