Redisson——浅析分布式锁之解锁
1. 解锁方法unlock()源码分析
@Override
public void unlock() {
try {
get(unlockAsync(Thread.currentThread().getId()));
} catch (RedisException e) {
if (e.getCause() instanceof IllegalMonitorStateException) {
throw (IllegalMonitorStateException) e.getCause();
} else {
throw e;
}
}
}
@Override
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromise<Void>();
// 删除redis中的锁,并发送解锁消息
RFuture<Boolean> future = unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
// 取消watchdog
cancelExpirationRenewal(threadId);
if (e != null) {
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;
}
result.trySuccess(null);
});
return result;
}
2. 解锁主流程
- 删除redis中的key,并且发布解锁消息
- 加锁的时候定义了解锁监听器(在上一篇加锁文章中讲述),收到解锁消息后执行解锁监听器任务
- 取消watchdog
整个redisson加解锁流程:
3. redis解锁unlockInnerAsync()
protected RFuture<Boolean> unlockInnerAsync(long threadId) {
return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " + // Hash key下是否有线程ID属性,不存在则返回null
"return nil;" +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " + // 存在则属性值-1后获取属性值,如果属性值大于0重置锁的过期时间,返回0
"if (counter > 0) then " +
"redis.call('pexpire', KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
"redis.call('del', KEYS[1]); " + // 否则删除锁,发布解锁消息,返回1
"redis.call('publish', KEYS[2], ARGV[1]); " +
"return 1; " +
"end; " +
"return nil;",
Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}
这段lua脚本的逻辑是:
- 锁名称下是否有属性等于线程ID,不存在返回null
- 存在的话,属性值减1,考虑重入锁,返回属性值counter
- 如果counter>0,则重置锁过期时间,返回0
- 如果counter=0,则删除锁,并发布解锁消息,返回1
4. 取消watchdog任务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()) {
Timeout timeout = task.getTimeout();
if (timeout != null) {
// 取消任务
timeout.cancel();
}
EXPIRATION_RENEWAL_MAP.remove(getEntryName());
}
}
世界那么大,感谢遇见,未来可期...
欢迎同频共振的那一部分人
作者公众号:Tarzan写bug