新开一个源码阅读系列,记录下自己阅读的源码
参考# 黑马程序员Redis入门到实战教程-P67等多个视频
数据结构
Hash结构
Value记录同一个锁的加锁次数,以满足可重入需求
可重入机制
- 加锁
org.redisson.RedissonLock#tryLockInnerAsync
"if (redis.call('exists', KEYS[1]) == 0) then " + key不存在
"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 " + key存在
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " + value++
"redis.call('pexpire', KEYS[1], ARGV[1]); " + 续期
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);", 加锁失败:返回key的剩余时间
- 解锁
org.redisson.RedissonLock#unlockInnerAsync
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " + key不存在
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + value--
"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;",
重试机制
分析代码org.redisson.RedissonLock#tryLock
- 尝试加锁,若加锁成功结束
- 若加锁失败 &&
waitTime > 0则订阅释放锁消息 - 收到释放锁消息 &&
waitTime > 0则再次尝试加锁 - 若加锁失败 &&
waitTime > 0则等待信号量
@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
// 判断订阅时间
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(threadId);
return false;
}
// 订阅释放锁消息
current = System.currentTimeMillis();
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
if (!await(subscribeFuture, time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
unsubscribe(subscribeFuture, threadId); // 取消订阅
}
});
}
acquireFailed(threadId);
return false;
}
// 尝试释放锁
try {
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(threadId);
return false;
}
while (true) {
long currentTime = System.currentTimeMillis();
ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(threadId);
return false;
}
// waiting for message 通过信号量等待
currentTime = System.currentTimeMillis();
if (ttl >= 0 && ttl < time) { // 找最小时间进行等待
getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
getEntry(threadId).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(threadId);
return false;
}
}
} finally {
unsubscribe(subscribeFuture, threadId);
}
// return get(tryLockAsync(waitTime, leaseTime, unit));
}
续约&&释放锁
- 续约通过看门狗实现
谁续约:拿到锁的线程续约
核心数据结构: org.redisson.RedissonLock#EXPIRATION_RENEWAL_MAP(private static final ConcurrentMap<String, ExpirationEntry>)
key是entryName即 Redission线程id+锁名称;value是ExpirationEntry
核心方法是org.redisson.RedissonLock#renewExpiration,不停递归执行
private void renewExpiration() {
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null) {
return;
}
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {
@Override
public void run(Timeout timeout) throws Exception {
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null) {
return;
}
Long threadId = ent.getFirstThreadId();
if (threadId == null) {
return;
}
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) -> {
if (e != null) {
log.error("Can't update lock " + getName() + " expiration", e);
return;
}
if (res) {
// reschedule itself 递归
renewExpiration();
}
});
}
}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS); // 默认10秒执行
ee.setTimeout(task);
}
上文获取锁的方法org.redisson.RedissonLock#tryAcquireAsync中有处理续约的scheduleExpirationRenewal方法
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId) {
if (leaseTime != -1) {
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
}
RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
ttlRemainingFuture.onComplete((ttlRemaining, e) -> {
if (e != null) {
return;
}
// lock acquired
if (ttlRemaining == null) {
scheduleExpirationRenewal(threadId); // 拿到锁的线程续约
}
});
return ttlRemainingFuture;
}
- 锁释放
org.redisson.RedissonLock#unlockAsync(long)
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromise<Void>();
RFuture<Boolean> future = unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
if (e != null) {
cancelExpirationRenewal(threadId);
result.tryFailure(e);
return;
}
if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
result.tryFailure(cause);
return;
}
cancelExpirationRenewal(threadId);
result.trySuccess(null);
});
return result;
}
org.redisson.RedissonLock#cancelExpirationRenewal中对任务进行取消
void cancelExpirationRenewal(Long threadId) {
ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (task == null) {
return;
}
if (threadId != null) {
task.removeThreadId(threadId);
}
if (threadId == null || task.hasNoThreads()) {
task.getTimeout().cancel(); // 取消任务
EXPIRATION_RENEWAL_MAP.remove(getEntryName()); // 删除
}
}