> 文章列表 > 模拟Redisson获取锁 释放锁 锁续命

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

模拟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()) {// 这把锁可重入次数+1redisLockInfo.reentry++;log.info("<您在之前已经获取过锁,锁直接可重入>");return true;}Long startTime = System.currentTimeMillis();Long expire = 30000L; //过期时间String lockId = UUID.randomUUID().toString(); //锁唯一idfor (; ; ) {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; //锁的idthis.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;}@Overridepublic 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. 锁一旦被释放, 就停止续命