文章目录
此处只以RLock.java的void lock(long leaseTime, TimeUnit unit)方法为例。
本文按代码逻辑顺序进行整理分析。
@Override
public void lock(long leaseTime, TimeUnit unit) {
try {
lockInterruptibly(leaseTime, unit);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
@Override
public void lockInterruptibly(long leaseTime, TimeUnit unit) throws InterruptedException {
long threadId = Thread.currentThread().getId();
//尝试获得锁,此方法也是redisson加锁的重点
Long ttl = tryAcquire(leaseTime, unit, threadId);
// 获得锁
if (ttl == null) {
return;
}
//订阅锁释放的消息。此处redis的机制如何实现的?我还没搞明白
RFuture<RedissonLockEntry> future = subscribe(threadId);
commandExecutor.syncSubscription(future);
try {
while (true) {
ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
break;
}
// 等到锁释放
if (ttl >= 0) {
getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
getEntry(threadId).getLatch().acquire();
}
}
} finally {
//取消订阅锁释放信息
unsubscribe(future, threadId);
}
}
private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId) {
/**
*此处调用了异步方法,然而用同步方式返回给上一段代码。我不明白这样的意义何在?
*/
return get(tryAcquireAsync(leaseTime, unit, threadId));
}
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {
//有传入leaseTime
if (leaseTime != -1) {
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
//由于未设置leaseTime,所以用redisson默认的30秒锁超时
RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(LOCK_EXPIRATION_INTERVAL_SECONDS, TimeUnit.SECONDS, threadId, RedisCommands.EVAL_LONG);
ttlRemainingFuture.addListener(new FutureListener<Long>() {
@Override
public void operationComplete(Future<Long> future) throws Exception {
if (!future.isSuccess()) {
return;
}
Long ttlRemaining = future.getNow();
// lock acquired
if (ttlRemaining == null) {
scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
}
tryLockInnerAsync()方法是redisson核心逻辑
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
internalLockLeaseTime = unit.toMillis(leaseTime);
//使用 EVAL 命令执行 Lua 脚本获取锁
//KEYS[1] :需要加锁的key,这里需要是字符串类型。
//ARGV[1] :锁的超时时间,防止死锁
//ARGV[2] :锁的唯一标识 id(UUID.randomUUID()) + ":" + threadId
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); " +/**value加1*/
"redis.call('pexpire', KEYS[1], ARGV[1]); " +/**重新设置超时时间*/
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}
上面的代码就是redisson进行加锁的主要代码。
下面是根据代码所画出的流程图。
本人理解可能会有偏差,错误之处,望各位指教。
注
- 文章是个人知识点整理总结,如有错误和不足之处欢迎指正。
- 如有疑问、或希望与笔者探讨技术问题(包括但不限于本章内容),欢迎添加笔者微信(o815441)。请备注“探讨技术问题”。欢迎交流、一起进步。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/69884.html