此处只以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 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 RFuture tryAcquireAsync(long leaseTime, TimeUnit unit, final long threadId) {
//有传入leaseTime
if (leaseTime != -1) {
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
//由于未设置leaseTime,所以用redisson默认的30秒锁超时
RFuture ttlRemainingFuture = tryLockInnerAsync(LOCK_EXPIRATION_INTERVAL_SECONDS, TimeUnit.SECONDS, threadId, RedisCommands.EVAL_LONG);
ttlRemainingFuture.addListener(new FutureListener() {
@Override
public void operationComplete(Future future) throws Exception {
if (!future.isSuccess()) {
return;
}
Long ttlRemaining = future.getNow();
// lock acquired
if (ttlRemaining == null) {
scheduleExpirationRenewal(threadId);
}
}
});
return ttlRemainingFuture;
}
tryLockInnerAsync()方法是redisson核心逻辑
RFuture tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand 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.
上面的代码就是redisson进行加锁的主要代码。
下面是根据代码所画出的流程图。
本人理解可能会有偏差,错误之处,望各位指教。
本人程序媛一枚,因为离港澳较近,周末兼职港澳人肉代购。
欢迎各位大佬添加本人微信,还会经常有点赞活动送价值不菲的小礼品哦。
即使现在不需要代购,等以后有了女(男)朋友、有了宝宝就肯定会需要的喽。
动动手指头,扫码一下,就当是对本博文的支持嘛。
微 信 号:YS334466888