Redisson分布式锁 --- 源码分析

本文详细描述了在分布式系统中使用Redisson库进行锁管理的过程,包括RLock的获取、业务代码中的加锁策略(tryAcquire和async版本)、看门狗锁续期机制、以及解锁操作,展示了如何通过lua脚本和定时任务实现高可用的锁管理。
摘要由CSDN通过智能技术生成

1.获取一把锁 

RLock lock = redissonClient.getLock("订单lock");

 

2.业务代码加锁

lock.lock();

 

 2.1 lock.tryAcquire

Long ttl = tryAcquire(leaseTime, unit, threadId);

2.2 tryAcquireAsync

1、调用tryLockInnerAsync方法,执行的是一个lua脚本,如果获取锁失败,返回的结果是这个key的剩余有效期,如果获取锁成功,则返回null。
2、如果获取锁成功ttlRemaining == null,并且leaseTime是默认值-1时,则执行scheduleExpirationRenewal(threadId);来启动看门狗机制

2.3 lua脚本: tryLockInnerAsync方法

如果获取锁失败,返回的结果是这个key的剩余有效期,如果获取锁成功,则返回null

getName="订单lock" 

getLockName(threadId) = "abb26a86-01bb-4cc1-945c-d274ae2fd454:70"  --->UUID:threadId

redis.exists:命令被用来检查键是否存在

若 key 存在返回 1 ,否则返回 0

redis.hset myhash field value   为哈希表中的field字段赋值

redis.pexpire 命令用于设置键(key)的有效期限(以毫秒为单位)

设置成功,返回 1

key 不存在或设置失败,返回 0

redis.hexists 命令用于查看哈希表的指定字段是否存在

1:包含字段;

0:不包含字段或键不存在

redis.hincrby myhash field 1

字段的增值操作后的值

redis.pttl 命令以毫秒为单位返回 key 的剩余过期时间

2.4 scheduleExpirationRenewal 

2.4.1一个锁就对应自己的一个ExpirationEntry类,EXPIRATION_RENEWAL_MAP存放所有的锁信息

2.4.2 EXPIRATION_RENEWAL_MAP里面获取锁,如果存在,则锁重入,如果不存在,则将新锁放入

 getEntryName() = UUID+lockName ---> abb26a86-01bb-4cc1-945c-d274ae2fd454:订单lock

2.5 renewExpiration 方法异步更新锁的过期时间 

1)从 EXPIRATION_RENEWAL_MAP 中获取与当前锁关联的 ExpirationEntry 对象。
2)如果未找到该对象,则直接返回。
3)使用 newTimeout 创建了一个定时任务

 

3.看门狗锁续期

renewExpirationAsync

1) 检查锁是否仍然存在,如果存在,则更新锁的过期时间为 internalLockLeaseTime,返回1表示成功;

2)如果不存在,则返回0表示失败

4.解锁

lock.unlock();
  • 如果释放锁过程中出现了异常,将异常抛出。
  • 如果释放锁的操作状态为 null,抛出 IllegalMonitorStateException 异常,表示尝试释放一个未被当前线程持有的锁。
  • 如果一切正常,则返回 null

4.1 解锁 -- unlockInnerAsync

 4.2 移除锁 --- cancelExpirationRenewal

  • EXPIRATION_RENEWAL_MAP 中获取与锁关联的 ExpirationEntry 对象。
  • 如果未找到该对象,直接返回。
  • 如果传入了 threadId,则移除该线程 ID。
  • 如果 threadId 为 null,或者锁不再被任何线程持有,则取消定时任务,并从 EXPIRATION_RENEWAL_MAP 中移除该锁的相关信息

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Redisson是一个基于RedisJava驻留库,提供了分布式和线程安全的Java数据结构。Redisson分布式锁实现是基于Redis的setnx命令和Lua脚本实现的。下面是Redisson分布式锁源码分析: 1.获取锁 Redisson分布式锁获取方法是tryAcquire方法,该方法首先会尝试使用setnx命令在Redis中创建一个key,如果创建成功则表示获取锁成功,否则会进入自旋等待。在自旋等待期间,Redisson会使用watchDog机制来监控锁的状态,如果锁被其他线程释放,则会重新尝试获取锁。 2.释放锁 Redisson分布式锁释放方法是release方法,该方法会使用Lua脚本来判断当前线程是否持有锁,如果持有锁则会使用del命令删除锁的key。 3.watchDog机制 Redisson的watchDog机制是用来监控锁的状态的,该机制会在获取锁时启动一个定时任务,定时任务会检查锁的状态,如果锁被其他线程释放,则会重新尝试获取锁。 ```java // 获取锁 public boolean tryAcquire(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { long time = unit.toMillis(waitTime); long current = System.currentTimeMillis(); final long threadId = Thread.currentThread().getId(); final long leaseTimeInMillis = unit.toMillis(leaseTime); while (true) { if (tryAcquire()) { scheduleExpirationRenewal(threadId, leaseTimeInMillis); return true; } time -= (System.currentTimeMillis() - current); if (time <= 0) { return false; } current = System.currentTimeMillis(); if (Thread.interrupted()) { throw new InterruptedException(); } // watchDog机制 RFuture<RedissonLockEntry> future = subscribe(threadId); if (!future.await(time, TimeUnit.MILLISECONDS)) { return false; } } } // 释放锁 public void unlock() { if (isHeldByCurrentThread()) { unlockInner(); } } private void unlockInner() { Long ttl = commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 0) then return nil end; " + "local counter = redis.call('hincrby', KEYS[1], ARGV[2], -1); " + "if (counter > 0) then return 0 end; " + "redis.call('del', KEYS[1]); " + "redis.call('publish', KEYS[2], ARGV[1]); " + "return 1;", Arrays.<Object>asList(getName(), getChannelName()), encode(new UnlockMessage(getName(), getLockName())), id); cancelExpirationRenewal(); if (ttl == null) { throw new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: " + id + " thread-id: " + Thread.currentThread().getId()); } if (ttl == -1) { get(lockName).deleteAsync(); } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值