Redisson分布式锁

发布于:2024-12-09 ⋅ 阅读:(118) ⋅ 点赞:(0)

官方文档:Redisson参考指南

分布式锁案例

RLock lock = redissonClient.getLock("test_lock");
lock.lock();
try {
    log.info("lock success");
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    lock.unlock();
}

Redisson分布式锁中的Lua脚本主要在两个核心方法中被调用:

tryAcquireAsync用于加锁。

unlockInnerAsync用于解锁。

源码:

加锁Lua脚本(tryAcquireAsync

org.redisson.RedissonLock#tryLockInnerAsync

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId,
 RedisStrictCommand<T> command) {
    return this.evalWriteAsync(this.getRawName(), LongCodec.INSTANCE, command, 
    "if (redis.call('exists', KEYS[1]) == 0) 
     then 
     redis.call('hincrby', KEYS[1], ARGV[2], 1); 
     redis.call('pexpire', KEYS[1], ARGV[1]); 
     return nil; 
     end; 
     if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) 
     then 
     redis.call('hincrby', KEYS[1], ARGV[2], 1); 
     redis.call('pexpire', KEYS[1], ARGV[1]); 
     return nil; 
     end; 
     return redis.call('pttl', KEYS[1]);", 
     Collections.singletonList(this.getRawName()), new Object[]{unit.toMillis(leaseTime), 
     this.getLockName(threadId)});
}
if (redis.call('exists', KEYS[1]) == 0) then
    redis.call('hset', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then 
    redis.call('hincrby', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
end;
return redis.call('pttl', KEYS[1]);

如果锁不存在(exists返回0),则创建锁并设置过期时间(pexpire)。如果锁已存在并且当前线程已持有锁(通过hexists检查),则增加重入次数(hincrby)并重置过期时间。

解锁Lua脚本(unlockInnerAsync

org.redisson.RedissonBaseLock#unlockInnerAsync

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
    return this.evalWriteAsync(this.getRawName(), LongCodec.INSTANCE, 
    RedisCommands.EVAL_BOOLEAN, 
    "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) 
    then 
    return nil;
    end; 
    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('del', KEYS[1]);
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1; 
    end; 
    return nil;", 
    Arrays.asList(this.getRawName(), this.getChannelName()), new Object[]{LockPubSub.UNLOCK_MESSAGE, this.internalLockLeaseTime, this.getLockName(threadId)});
}
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    return nil;
end;
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('del', KEYS[1]);
    redis.call('publish', KEYS[2], ARGV[1]);
    return 1;
end;
return nil;

        如果锁存在并且是当前线程持有的(通过hexists检查),则减少重入次数(hincrby)。如果重入次数大于0,则更新过期时间(pexpire)。如果重入次数为0,则删除锁(del)并通过publish发布解锁消息,以便唤醒等待的线程。