Redis高并发分布式锁详解( 四 ) 2026-03-02 生活百科 (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下一页 推荐阅读 双子座彻底放弃你的表现是什么? 做仰卧起坐请避开这些误区! 真情告白的句子白话 深情的告白句子大白话 手机QQ好友管理器系统教程推荐 玉米的保存方法有哪些 玉米应该怎么保存? 盆栽茶花如何养护呢?知道这些方法 如何获取花呗收款二维码? 支付宝收款码在哪里找 用人单位未参保工伤保险 职工发生工伤需全赔 蒜蓉蒸虾要蒸多少分钟 蒜蓉蒸虾的做法蒸多久? 极融借款 极融 红烧肉的营养成分有那些 成都到九寨沟多远距离 成都到九寨沟多远 卡农是谁作的曲 北京密云9路公交车路线 北京密云旅游公交专线91路发车时间 光遇里的幽光山洞在哪里 怎样卤肉颜色好看好吃 怎么样卤肉颜色鲜亮 广东徐闻菠萝2毛钱一斤 2017徐闻菠萝价格 关于现代公共管理教材系列·政治学概论简述 现代公共管理教材系列·政治学概论 电脑屏幕截屏方法 如何电脑屏幕截屏 整箱白云边酒里面有包装袋吗 小米手环6血氧监测准吗_小米手环6血氧监测准确度高吗 怎样摇骰子怎么玩点数(摇骰子的高级技巧) 天玑900对比麒麟985哪款性能更高? 24 《吐血整理》高级系列教程-吃透Fiddler抓包教程-Fiddler如何优雅地在正式和测试环境之间来回切换-中篇 2000左右高性价比5G手机排行榜-2000元值得买的5g手机排行榜 原生Redis跨数据中心双向同步优化实践 小米10系列有几款_小米10系列哪个性价比高 跟我学Python图像处理丨傅里叶变换之高通滤波和低通滤波 之三 2流高手速成记:SpringBoot整合mybatis/mybatis-plus实现数据持久化 如何打麻将(麻将高手打牌思路技巧)