模拟Redisson获取锁 释放锁 锁续命

获取锁


Long timeout = 3000L;//获取锁超时时间
Map<Thread, RedisLockInfo> lockCacheMap = new ConcurrentHashMap<>();//本地缓存锁

public boolean tryLock() {
        //获取当前线程
        Thread cuThread = Thread.currentThread();
    	//先从本地缓存中获取锁实例
        RedisLockInfo redisLockInfo = lockCacheMap.get(cuThread);
        if (redisLockInfo != null && redisLockInfo.isState()) {
            // 这把锁可重入次数+1
            redisLockInfo.reentry++;
            log.info("<您在之前已经获取过锁,锁直接可重入>");
            return true;
        }
        
        Long startTime = System.currentTimeMillis();
        Long expire = 30000L; //过期时间
        String lockId = UUID.randomUUID().toString(); //锁唯一id
        for (; ; ) {
            Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(redisLockKey, lockId, expire, TimeUnit.SECONDS);
            if (lock) {
                log.info("<获取锁成功>");
                RedisLockInfo newRedisLockInfo = new RedisLockInfo(lockId, cuThread, expire);
                lockCacheMap.put(cuThread, newRedisLockInfo);
                // 开启锁续命线程
                new Thread(new LifeExtensionThread(newRedisLockInfo, 10000)).start();
                return true;
            }
            // 获取锁失败, 控制再次获取的超时时间
            Long endTime = System.currentTimeMillis();
            if (endTime - startTime > timeout) {
                log.info("<重试的时间已经过了,不能够在继续重试啦>");
                return false;
            }
        }

    }

锁实例

 public RedisLockInfo(String lockId, Thread lockThread, Long expire, Long reentry) {
        state = true;//锁状态, true-获取锁
        this.lockId = lockId; //锁的id
        this.lockThread = lockThread; //持有锁的线程
        this.expire = expire; //过期时间
        lifeCount = new AtomicInteger(0); // 续期次数, 原子操作
        this.reentry = reentry; //重试次数
    }

说明:

  1. 获取锁后, 记录锁实例信息, 并缓存到本地

  2. 控制获取锁的时间, 避免过去消耗cpu资源


释放锁

public boolean releaseLock() {
        //本地缓存查找当前线程锁实例
        RedisLockInfo redisLockInfo = lockCacheMap.get(Thread.currentThread());
        if (redisLockInfo == null) {
            return false;
        }
        String redisLockId = stringRedisTemplate.opsForValue().get(redisLockKey);
        if (StringUtils.isEmpty(redisLockId)) {
            log.error("该key已经过期或者不存在");
            return false;
        }
        //线程删除各自的锁
        String lockId = redisLockInfo.getLockId();
        if (!lockId.equals(redisLockId)) {
            log.error("不是当前我自己线程调用删除key");
            return false;
        }
        return stringRedisTemplate.delete(redisLockKey);
    }

说明:

在释放锁的时候, 会把当前线程lockid和redis中的redisLockId对比, 是为了防止误删

假设a线程业务时长20s, 锁时长10s, 那么在第10s的时候, 锁会被释放;

这时b线程获取锁, 当第20s时候, a线程执行完业务, 开始释放锁, 实际上释放的是b线程的锁

这时c线程获取锁...


锁续命

class LifeExtensionThread implements Runnable {
        private RedisLockInfo redisLockInfo;
        private int interval;

        public LifeExtensionThread(RedisLockInfo redisLockInfo, int interval) {
            this.redisLockInfo = redisLockInfo;
            this.interval = interval;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    // 每隔一段时间实现续命
                    Thread.sleep(interval);
                    // 锁实例为空, 不再续命
                    if (redisLockInfo == null) {
                        return;
                    }
                    //当前线程执行完毕, 锁已释放
                    Thread lockThread = redisLockInfo.getLockThread();
                    if (redisLockInfo.isState() && lockThread.isInterrupted()) {
                        log.info(">>当前获取到锁的线程,已经执行完毕啦, 锁已经释放啦。<<");
                        return;
                    }
                    //锁 对应线程还在一直使用 没有释放
                    Integer lifeCount = redisLockInfo.getLifeCount();
                    // 续命次数如果超过3次以上停止
                    if (lifeCount > 3) {
                        log.info(">>续命的次数已经达到了次数,开始主动释放锁<<");
                        // 1.回滚当前事务
                        //2.释放锁
                        stringRedisTemplate.delete(redisLockKey);
                        //3.该拿到锁的线程应该主动停止掉
                        lockThread.interrupt();
                        return;
                    }
                    // 延长过期key的时间, 结合lua脚本续命
                    stringRedisTemplate.expire(redisLockKey, redisLockInfo.getExpire(),TimeUnit.SECONDS); 
                } catch (Exception e) {
                    log.error(">>e:{e}<<", e);
                }
            }
        }
    }

说明:

  1. 开启一个线程死循环, 只要当前线程锁没有被释放, 就续命

  2. 可设置续命频率(默认10s), 续命时长(默认30s)和续命次数

  3. 锁一旦被释放, 就停止续命

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
循环获取释放Redisson是为了实现超时续约的机制。当一个线程获取后开始执行业务逻辑,但由于业务执行时出现卡顿,导致超时后被释放。此时,如果业务逻辑还未执行完毕,其他线程可能会获取到分布式并开始执行,从而引发线程安全问题。为了避免这种情况,需要循环获取并续约,直到业务逻辑执行完毕后释放。 在Redisson中,可以使用`tryLock()`方法来尝试获取,如果获取成功,则可以执行业务逻辑。在业务逻辑执行完毕后,需要调用`unlock()`方法来释放。如果获取失败,则可以根据需要进行相应的处理。 在给定的代码示例中,`method1()`和`method2()`分别代表两个不同的方法,它们都尝试获取并执行业务逻辑。如果获取失败,则会输出相应的错误信息。在`finally`块中,会调用`unlock()`方法来释放。 需要注意的是,循环获取释放Redisson的具体实现可能会根据业务需求和代码逻辑而有所不同。以上提供的代码示例仅供参考。 #### 引用[.reference_title] - *1* *3* [《Redis实战篇》五、分布式-redission](https://blog.csdn.net/LXYDSF/article/details/128439761)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [模拟Redisson获取 释放 续命](https://blog.csdn.net/xiaoshitou_2015/article/details/129987957)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值