一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
1 前言
研究源码 是为了解决实际问题 - 雨夜
zl(沈阳)-皓哥 是我目前遇到的最好的老大
先看 思考和应用场景的问题 思考下,然后再继续往下看
如果可以的话 点个赞/评论下 哈哈 好有点动力 有问题 也请留言
2 上节回顾/本节重点/上节思考题
2.1 上节
讲了加锁/watch dog 额外还有时间轮
2.2 本节讲解
1. 调用unlock 主动释放锁
2 宕机自动释放锁
3 思考
4 应用场景
5 unlock 步骤
5.1 RedissonLock 类 unlockInnerAsync方法
5.1.1
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;
5.1.2 方法执行之后 参数
RedissonLock.EXPIRATION_RENEWAL_MAP
key 例子 0dd5078f-f36a-4eaa-bf45-43b50871de62:lockKeyForWatchDog
value RedissonLock.ExpirationEntry
5.2 参数
KEYS[1] lockKey
KEYS[2] getChannelName
实际 redisson_lock__channel:{lockKey}
ARGV[1] LockPubSub.UNLOCK_MESSAGE
实际 0
ARGV[2] this.internalLockLeaseTime
实际 30000
ARGV[3] this.getLockName(threadId)
07275c80-686c-4160-ac10-898def01d893:1
5.3 参数带入 解析
# 如果没锁过lockKey 直接返回null\
if (redis.call('hexists', lockKey, 07275c80-686c-4160-ac10-898def01d893:1) == 0) then \
return nil;\
end;\
# 计数器 unlock 会把value 减一 也就是解一次锁\
local counter = redis.call('hincrby', lockKey, 07275c80-686c-4160-ac10-898def01d893:1, -1); \
# 解一次锁之后 锁还被使用着\
if (counter > 0) then \
# lockKey 重置时间\
redis.call('pexpire', lockKey, 30000);\
return 0; \
else \
# 锁被释放了 删除lockKey\
redis.call('del', lockKey); \
redis.call('publish', redisson_lock__channel:{lockKey}, lockKey); \
return 1; \
end;\
return nil;
5.4 收获
锁不存在 返回null
没解开 返回0 也就是false
锁解开了 返回1 也就是 true
5.5 里面有一个publish
redis.call('publish', redisson_lock__channel:{lockKey}, lockKey)
这是干什么的 有什么用?
广播解锁消息,去唤醒那些争抢过锁但还处于阻塞中的线程
6 基本概念
7 前面思考和应用场景解答 (在此之前 请再次思考前面的问题 )
7.1 多次解锁同一个lockKey 有什么返回
RLock rLock = redissonClient.getLock("lockKey");
rLock.lock();
rLock.unlock();
rLock.unlock();
返回信息
java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: e2b12746-6961-4d7b-94d8-cc9e309dc410 thread-id: 1
报错的原因
fa
public RFuture<Void> unlockAsync(long threadId) {
RPromise<Void> result = new RedissonPromifase();
//这就是那个lua 返回值请看5.4
RFuture<Boolean> future = this.unlockInnerAsync(threadId);
future.onComplete((opStatus, e) -> {
this.cancelExpirationRenewal(threadId);
if (e != null) {
result.tryFailure(e);
//**opStatus 就是lua返回值 如果为null 代表 没有这个lockKey**
} else if (opStatus == null) {
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + this.id + " thread-id: " + threadId);
result.tryFailure(cause);
} else {
result.trySuccess((Object)null);
}
});
return result;
}
7.2 如果解锁的是别人的锁 什么返回
返回信息 Exception in thread "Thread-8" java.lang.IllegalMonitorStateException: attempt to unlock lock, not locked by current thread by node id: eccf8054-4f90-4b59-8c07-aabd71dfb179 thread-id: 65
报错原因和上面一摸一样
收获
解锁 无论多次解锁还是 解错lockKey 都会报错,可能在工作中就会存在解锁失败 导致的事务回滚问题
8 总结:
9 下节预知
- 公平锁 讲解
之后添加的额外内容:(先记下 定期添加) 已完成 1 时间轮(单机/分布式 和 对应的应用场景) 2 redission 面试题解析
10 目标:
- 随着学习 把一些坑解决 形成一个 redission的基础组件
代码地址: gitee.com/gf-8/yuye-p…
项目: yuye-test-redission
类地址: 对应的test 包之下 UnLockApplicationTests类
11 思考题
本节说了 要防止lockKey 解锁带来的回滚事务等问题,那怎么才能避免呢?为什么能避免?