Redis高并发分布式锁详解( 四 ) 2026-09-30 生活百科 (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下一页 推荐阅读 胃不好吃什么东西可以养胃 哪些食物养胃 脱蜡铸造法 脱蜡铸造的流程 根本没人敢侵犯这些人!脾气不大,但手段过人的3个星座 高铁可以带两个箱子吗 高铁能带两个箱子吗 如何治疗小儿肾病 华坪是哪个省 梨子怎么画 梨子怎么画简笔画图片 单引号怎么打 单引号和双引号区别 绿茶怎么分辨好不好 绿茶怎么分辨好不好喝 元素周期表哪些是主族 元素周期表主族元素有哪些 女人吃鸽子蛋的好处有哪些 鸽子蛋的营养价值高吗 离心机的功能和用途 狙击精英5工作台有哪些 狙击精英5配置 枪杆好还是直杆好 路亚竿枪柄和直柄哪个好用 银行按揭怎么做 银行按揭 红酸枝黑酸枝哪个档次高 黑酸枝排名 梭子蟹能和红枣一起吃吗 梭子蟹能和红枣一起吃吗? 穗岁康保险优缺点 穗岁康保险怎么买 禄刃格、比劫格、建禄格八字命理分析 牛年范姓男孩起名大全 霸气高冷名字推荐 小米手环6血氧监测准吗_小米手环6血氧监测准确度高吗 怎样摇骰子怎么玩点数(摇骰子的高级技巧) 天玑900对比麒麟985哪款性能更高? 24 《吐血整理》高级系列教程-吃透Fiddler抓包教程-Fiddler如何优雅地在正式和测试环境之间来回切换-中篇 2000左右高性价比5G手机排行榜-2000元值得买的5g手机排行榜 原生Redis跨数据中心双向同步优化实践 小米10系列有几款_小米10系列哪个性价比高 跟我学Python图像处理丨傅里叶变换之高通滤波和低通滤波 之三 2流高手速成记:SpringBoot整合mybatis/mybatis-plus实现数据持久化 如何打麻将(麻将高手打牌思路技巧)