官方文档: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) thenredis.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) thenreturn nil;
end;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) thenredis.call('pexpire', KEYS[1], ARGV[2]);return 0;
elseredis.call('del', KEYS[1]);redis.call('publish', KEYS[2], ARGV[1]);return 1;
end;
return nil;
如果锁存在并且是当前线程持有的(通过hexists
检查),则减少重入次数(hincrby
)。如果重入次数大于0,则更新过期时间(pexpire
)。如果重入次数为0,则删除锁(del
)并通过publish
发布解锁消息,以便唤醒等待的线程。