Redis高并发分布式锁详解( 四 ) 2026-02-20 生活百科 (newPromise));return;}// 3:创建一个监听器,别的线程进行redis-pub命令之后进行调用RedisPubSubListener listener = createListener(channelName, value);// 4:底层交给netty调用redis-sub命令subscribeService.subscribe(LongCodec.INSTANCE, channelName, semaphore, listener);}};semaphore.acquire(listener);listenerHolder.set(listener);return newPromise;}3)RedissonLock类#tryAcquireAsync方法(核心点主体) //RedissonLock类#tryAcquireAsync方法private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, final 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.addListener(new FutureListener<Long>() {@Override//Future任务执行完会回调该方法public void operationComplete(Future<Long> future) throws Exception {if (!future.isSuccess()) {return;}Long ttlRemaining = future.getNow();// 加锁成功if (ttlRemaining == null) {//看门狗续命scheduleExpirationRenewal(threadId);}}});return ttlRemainingFuture;}4)RedissonLock类#tryLockInnerAsync方法(核心点,加锁逻辑) //RedissonLock类#tryLockInnerAsync方法//利用redis的单线程执行任务,redis会将整个脚本作为一个整体执行 , 且中间不会被其他命令插入//采用的是hash的类型来存储锁 , 为了实现重入锁的概念//Redis pttl命令以毫秒为单位返回 key 的剩余过期时间<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);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); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +"return redis.call('pttl', KEYS[1]);",//对应为KEYS[1](对应传入的锁的命名),ARGV[1](设置的超时时间,默认30s),ARGV[2] -》(uuid + ":" + threadId)Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}5)RedissonLock类#scheduleExpirationRenewal方法(核心点,看门狗的逻辑【续命】) //RedissonLock类#scheduleExpirationRenewal方法//采用Future+事件监听的方式,方法嵌套调用来实现定时任务private void scheduleExpirationRenewal(final long threadId) {if (expirationRenewalMap.containsKey(getEntryName())) {return;}Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {RFuture<Boolean> future = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;",Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));//再次添加监听器,重复检查future.addListener(new FutureListener<Boolean>() {@Overridepublic void operationComplete(Future<Boolean> future) throws Exception {expirationRenewalMap.remove(getEntryName());if (!future.isSuccess()) {log.error("Can't update lock " + getName() + " expiration", future.cause());return;}if (future.getNow()) {// reschedule itself//递归调用scheduleExpirationRenewal(threadId);}}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);//如果该任务已经存在一个了,就把新建的任务关闭,Map中的key为(uuid + ":" + threadId)if (expirationRenewalMap.putIfAbsent(getEntryName(), task) != null) {task.cancel();}}6)Redisson类#unlock方法 //RedissonLock类#unlock方法public void unlock() {Boolean opStatus = get(unlockInnerAsync(Thread.currentThread().getId()));if (opStatus == null) {throw new IllegalMonitorStateException(...);}if (opStatus) {//移除看门狗的定时任务cancelExpirationRenewal();}}//RedissonLock类#unlockInnerAsync方法protected RFuture<Boolean> unlockInnerAsync(long threadId) {return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,//如果不存在锁"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1; " +"end;" +//当前线程并没有持有锁,则返回nil"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +"return nil;" +"end; " +//前线程持有锁,则对value-1,拿到-1之后的vlaue"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +//value>0,以毫秒为单位返回剩下的过期时间 。(保证可重入)"if (counter > 0) then " +"redis.call('pexpire', KEYS[1], ARGV[2]); " +"return 0; " +//value<=0,则对key进行删除操作,return 1 (方法返回 true) 。然后进行redis-pub指令,用于唤醒其他正在休眠的线程 。"else " +"redis.call('del', KEYS[1]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1; "+"end; " +"return nil;",//参数顺序KEYS[1](锁的名称),KEYS[2](发布订阅的Channel名:redisson_lock__channel+锁名),ARGV[1](发布的消息),ARGV[2](锁超时时间),ARGV[3](uuid + ":" + threadId)Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId));} 上一页123456下一页 推荐阅读 世界纪录水下憋气保持者,就像是可以在水下呼吸一样不可思议。 ... 社保和劳动合同不一致吗 情人泪水培方法 情人泪如何水培 【养生知识】太极拳的姿势要求 属虎的属猪的能婚配吗,正月属猪人和三月属虎人相配吗 2015年能修成正果的星座配对 空腹吃鱼?等于慢性自杀! 罗倩文测试分数118分 蟋蟀的住宅表现了蟋蟀的什么精神 雨字五行属什么 雨字五行属什么行 北京s2线看花坐到哪下?赏花打卡地推荐 哪些食物生吃最好 不能生吃的食物有哪些 想要下载小说,首先打开网页使用搜索引擎查找小说 怎么下载小说 什么礼物适合送给女生,盘点几款有创意的 侄子满月送什么礼物,安利一波有逼格的 怎样看待毕婚族? 毕婚族是什么意思 浅色衣服发黄清洗窍门 浅色棉服发黄清洗窍门 埃沃定制衬衫一般在什么价位呢?大概有怎样的款式 儿童自闭症原因 儿童自闭症好治疗吗 2022年金木水火土五行查询,2021年金木水火土五行查询表 小米手环6血氧监测准吗_小米手环6血氧监测准确度高吗 怎样摇骰子怎么玩点数(摇骰子的高级技巧) 天玑900对比麒麟985哪款性能更高? 24 《吐血整理》高级系列教程-吃透Fiddler抓包教程-Fiddler如何优雅地在正式和测试环境之间来回切换-中篇 2000左右高性价比5G手机排行榜-2000元值得买的5g手机排行榜 原生Redis跨数据中心双向同步优化实践 小米10系列有几款_小米10系列哪个性价比高 跟我学Python图像处理丨傅里叶变换之高通滤波和低通滤波 之三 2流高手速成记:SpringBoot整合mybatis/mybatis-plus实现数据持久化 如何打麻将(麻将高手打牌思路技巧)