基于Redisson的Redis分布式锁实战

java Terry 336浏览 1评论

依赖版本:

<dependency>
   <groupId>org.redisson</groupId>
   <artifactId>redisson</artifactId>
   <version>3.11.0</version>
</dependency>

基于Redis的Redisson分布式锁RLock对象实现了java.util.concurrent.locks.Lock接口。

Config config = Config.fromYAML(new File(ClassLoader.getSystemResource("redis.yaml").toURI()));
RedissonClient redissonClient = Redisson.create(config);
RLock lock = redissonClient.getLock("lock1");
boolean res = lock.tryLock(20,10, TimeUnit.SECONDS);//尝试加锁,最多等待20秒,上锁以后10秒自动解锁
if(res){
   try {
      System.out.println("已获取锁:"+lock.getName());
   }finally {
      lock.unlock();
      }
}

现在分析下加锁实现

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                  "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]);",
                    Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
    }

通过lua脚本实现加锁逻辑,先判断key是否存在,不存在则调用hset存储当前线程信息并且设置过期时间,返回nil,告诉客户端直接获取到锁。存在则将重入次数加1,并重新设置过期时间,返回nil,告诉客户端直接获取到锁。如果被其它线程已经锁定,返回锁有效期的剩余时间,告诉客户端需要等待。

解锁过程

protected RFuture<Boolean> unlockInnerAsync(long threadId) {
        return commandExecutor.evalWriteAsync(getName(), 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.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));

    }
  1. 如果lock键不存在,发消息说锁已经可用
  2. 如果锁不是被当前线程锁定,则返回nil
  3. 由于支持可重入,在解锁时将重入次数需要减1
  4. 如果计算后的重入次数>0,则重新设置过期时间
  5. 如果计算后的重入次数<=0,则发消息说锁已经可用

转载请注明:Terry's blog » 基于Redisson的Redis分布式锁实战

发表我的评论
取消评论
表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 👍👍👍
    Supergan2个月前 (07-13)回复