目录

一、Redisson的引入

1、不可重入

2、不可重试

3、超时释放

4、主从一致性

二、Redisson

1、什么是Redisson?

2、Redisson使用手册

3、Redisson快速入门(Demo)

(1)导依赖

(2)配置Redisson客户端

(3)使用Redisson的分布式锁

三、Redisson实现可重入锁

1、Redisson 解决重入问题

获取锁

释放锁

测试代码

四、Redisson实现重试、超时续约问题

实现重试、超时续约问题流程

1、API参数

重试、超时续约的源码分析

(1)锁重试

(2)锁超时续约

五、Redisson实现主从一致性问题

1、问题描述

2、Redisson的解决方案——MultiLock

联锁的使用

为什么使用redissonClient.getMultiLock(...)就行?

MultiLock源码分析 !!!

关于Redisson分布式锁的可重入、重试、联锁问题的总结


一、Redisson的引入

我们先来看看之前的基于setnx实现的分布式锁存在的问题:

我们之前实现的分布式锁是基于redis的setnx命令的特性的!

但是,这样子实现起来会有很多弊端!

1、不可重入

简单的来说就是一旦setnx [key] [value]后,就不能再对这个key做任何操作了(除了删除)

假设我们在开发中有A和B两个业务,在业务A中,执行了setnx操作,然后在业务A中调用业务B。然后在业务B中也有setnx的操作(同一个KEY),此时,业务B就会阻塞在这里,等待业务A释放锁,但是,业务A肯定不会释放锁,因为业务A还没有执行完(调B)。故就会发生死锁

2、不可重试

在我们之前业务逻辑中,尝试获取锁,如果获取不到就直接return了,没有“重来”的机会!也无法提供重试的机制!

3、超时释放

我们之前,分析过分布式锁被误删的问题。这个问题是已经解决了。

但是,仍然会存在隐患!我们这里是用TTL来控制它。业务执行,时间多少,这是一个未知数,TTL要怎么设置?如何处理业务阻塞?

4、主从一致性

主节点上获取到了锁,但是主节点突然宕机了,就会从从结点中选出一个节点,作为主节点。

但由于,因为之前的那个主节点宕机了。在新选举出来的这个主节点中是无法获取到之前的锁。

所以之前的那个锁相当于失效了!


二、Redisson

要解决上述问题并不是那么容易的,如果我们自己实现很有可能会出一些问题!所以最好的办法就是使用市面上的一些框架来解决!

1、什么是Redisson?

        Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务,其中就包含了各种分布式锁的实现。

2、Redisson使用手册

Redission使用手册https://www.bookstack.cn/read/redisson-wiki-zh/Redisson%E9%A1%B9%E7%9B%AE%E4%BB%8B%E7%BB%8D.md里面提到了Redisson可以实现大致如下的分布式锁

3、Redisson快速入门(Demo)

(1)导依赖

<!-- redis-redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version>
</dependency>

(2)配置Redisson客户端

/*** 配置 Redisson*/
@Configuration
public class RedisConfig {@Beanpublic RedissonClient redissonClient() {// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.56.20:6379").setPassword("123456");// 创建 RedissonClient 对象return Redisson.create(config);}
}

(3)使用Redisson的分布式锁

@Test
void testRedisson() throws Exception {RLock anyLock = redissonClient.getLock("anyLock");boolean isLock = anyLock.tryLock(1, 10, TimeUnit.SECONDS);if(isLock) {try {System.out.println("执行业务");} finally {anyLock.unlock();}}
}

测试结果


三、Redisson实现可重入锁

这里可重入锁的实现 和 Java的 ReentrantLock 类似!

获取锁的时候,先判断是不是同一个对象,是就将 value+1,释放锁的时候就 value-1,当其小于0时就将该key删除!(Redisson帮我们做好了)

在Redis中使用 Hash结构 去存储!

1、Redisson 解决重入问题

获取锁

@Override
public boolean tryLock() {return get(tryLockAsync());
}
@Override
public RFuture<Boolean> tryLockAsync() {return tryLockAsync(Thread.currentThread().getId());
}
private RFuture<Boolean> tryAcquireOnceAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {// 锁自动释放的时间,这里没有传,默认 -1if (leaseTime != -1) {return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_NULL_BOOLEAN);}// tryLockInnerAsync 具体实现RFuture<Boolean> ttlRemainingFuture = tryLockInnerAsync(waitTime,commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_NULL_BOOLEAN);ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e != null) {return;}if (ttlRemaining) {scheduleExpirationRenewal(threadId);}});return ttlRemainingFuture;
}

tryLockInnerAsync() 里面是通过一个Lua脚本来实现获取锁!

<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);// 在Lua脚本中起始位是1return evalWriteAsync(getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hincrby', 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]);",Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

上述代码中字符串部分就是Lua脚本,Redisson用其实现可重入锁

Redisson 获取锁中的Lua脚本源码解析

-- 判断锁是否存在
if (redis.call('exists', KEYS[1]) == 0) then-- 不存在,获取锁redis.call('hincrby', 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-- 自增+1redis.call('hincrby', KEYS[1], ARGV[2], 1);-- 重置有效期redis.call('pexpire', KEYS[1], ARGV[1]);return nil;
end;
return redis.call('pttl', KEYS[1]);

释放锁

protected RFuture<Boolean> unlockInnerAsync(long threadId) {return evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +"return nil;" +"end; " +"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +"if (counter > 0) then " +"redis.call('pexpire', KEYS[1], ARGV[2]); " +"return 0; " +"else " +"redis.call('del', KEYS[1]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1; " +"end; " +"return nil;",Arrays.asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}

Redisson 释放锁中的Lua脚本源码解析

-- 判断当前锁是否还是被自己持有
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then-- 不是就就直接返回return nil;end;
-- 是自己,则重入次数 -1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
-- 判断重入次数是否已经为0
if (counter > 0) then-- 大于0,说明不能释放,重置有效期即可redis.call('pexpire', KEYS[1], ARGV[2]);return 0;
else-- 等于0,说明可以直接删除redis.call('del', KEYS[1]);-- 发消息redis.call('publish', KEYS[2], ARGV[1]);return 1;end;
return nil;

测试代码

可重入锁的原理就如同上面描述的那样,但是单纯使用起来我们只要调 lock.tyrLock() 的API就行了,无需关注那么多!

我们这边模拟一下锁重入的场景。方法A上锁后调方法B,方法B也获取锁(如果是不可重入,这里就会阻塞!)

/*** Redisson的单元测试*/
@SpringBootTest
@Slf4j
public class RedissonTest {@Resourceprivate RedissonClient redissonClient;private RLock lock;@BeforeEachvoid setUp() {lock = redissonClient.getLock("order");}@Testvoid method1() {boolean isLock = lock.tryLock();if (!isLock) {log.error("获取锁失败 ... 1");return;}try {log.info("获取锁成功 ... 1");method2();log.info("开始执行业务 ... 1");} finally {log.warn("准备释放锁 ... 1");lock.unlock();}}@Testvoid method2() {boolean isLock = lock.tryLock();if (!isLock) {log.error("获取锁失败 ... 2");return;}try {log.info("获取锁成功 ... 2");log.info("开始执行业务 ... 2");} finally {log.warn("准备释放锁 ... 2");lock.unlock();}}
}

运行结果 

Redis 中值的情况 


四、Redisson实现重试、超时续约问题

实现重试、超时续约问题流程

  • 可重试:利用信号量和PubSub功能实现等待、唤醒,获取锁失败的重试机制
  • 超时续约:利用watchDog,每隔一段时间(releaseTime / 3),重置超时时间

1、API参数

waitTime:最大等待时间,如果使用 tryLock() 的时候,有传参数表明是可重试的锁;反之,不是!

leaseTime:超时释放时间,默认是-1,建议不要设定,Redisson看门狗机制可以就行锁续约

重试、超时续约的源码分析

(1)锁重试

@Override
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {return tryLock(waitTime, -1, unit);
}

tryLock() 

里面实现了重试机制!通过消息订阅信号量机制,避免了 while(true) 让其一直无效尝试,避免了CPU空转问题

@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {// 转成毫秒,后面都是以毫秒为单位long time = unit.toMillis(waitTime);// 当前时间long current = System.currentTimeMillis();// 线程ID-线程标识long threadId = Thread.currentThread().getId();// 尝试获取锁 tryAcquire() !!!Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);// 如果上面尝试获取锁返回的是null,表示成功;如果返回的是时间则表示失败。if (ttl == null) {return true;}// 剩余等待时间 = 最大等待时间 -(用现在时间 - 获取锁前的时间)time -= System.currentTimeMillis() - current;// 剩余等待时间 < 0 失败if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}// 再次获取当前时间current = System.currentTimeMillis();// 重试逻辑,但不是简单的直接重试!// subscribe是订阅的意思RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);// 如果在剩余等待时间内,收到了释放锁那边发过来的publish,则才会再次尝试获取锁if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {   // 取消订阅unsubscribe(subscribeFuture, threadId);}});}// 获取锁失败acquireFailed(waitTime, unit, threadId);return false;}try {// 又重新计算了一下,上述的等待时间time -= System.currentTimeMillis() - current;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}// 重试!while (true) {long currentTime = System.currentTimeMillis();ttl = tryAcquire(waitTime, leaseTime, unit, threadId);// 成功if (ttl == null) {return true;}// 又获取锁失败,再次计算上面的耗时time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}currentTime = System.currentTimeMillis();// 采用信号量的方式重试!if (ttl >= 0 && ttl < time) {subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}// 重新计算时间(充足就继续循环)time -= System.currentTimeMillis() - currentTime;if (time <= 0) {acquireFailed(waitTime, unit, threadId);return false;}}} finally {unsubscribe(subscribeFuture, threadId);}
}

在 tryLock() 中调 tryAcquire() 执行获取锁的操作

private Long tryAcquire(long waitTime, long leaseTime, TimeUnit unit, long threadId) {return get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));
}
private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId) {if (leaseTime != -1) {return tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);}// leaseTime我们没有传,这里设定默认值(看门狗)30sRFuture<Long> ttlRemainingFuture = tryLockInnerAsync(waitTime,commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);// 回调函数 ttlRemaining:剩余有效期,e:异常ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e != null) {return;}// 剩余有效期为null,表示获取锁成功!if (ttlRemaining == null) {// 锁续约scheduleExpirationRenewal(threadId);}});return ttlRemainingFuture;
}
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {// 把 leaseTime锁释放时间 记录成一个本地的成员变量internalLockLeaseTime = unit.toMillis(leaseTime);// 获取锁成功返回nil(空),失败返回时间,锁的剩余有效期(pttl是以毫秒为单位)return evalWriteAsync(getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hincrby', 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]);",Collections.singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}

(2)锁超时续约

private void scheduleExpirationRenewal(long threadId) {RedissonLock.ExpirationEntry entry = new RedissonLock.ExpirationEntry();/* putIfAbsent() 是ConcurrentHashMap的API* (1)如果是新的记录,那么会向map中添加该键值对,并返回null* (2)如果已经存在,那么不会覆盖已有的值,直接返回已经存在的值* EXPIRATION_RENEWAL_MAP 是静态的,key为锁的名称*/RedissonLock.ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);// 新的、旧的都会加if (oldEntry != null) {oldEntry.addThreadId(threadId);} else {entry.addThreadId(threadId);// 新的,还会多一步操作(更新有效期)renewExpiration();}
}

renewExpiration

private void renewExpiration() {RedissonLock.ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ee == null) {return;}Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask() {@Overridepublic void run(Timeout timeout) throws Exception {RedissonLock.ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (ent == null) {return;}Long threadId = ent.getFirstThreadId();if (threadId == null) {return;}// 刷新有效期RFuture<Boolean> future = renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (e != null) {log.error("Can't update lock " + getName() + " expiration", e);return;}if (res) {// 自己调自己renewExpiration();}});}}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);ee.setTimeout(task);
}

刷新有效期(重置)

protected RFuture<Boolean> renewExpirationAsync(long threadId) {return 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.singletonList(getName()),internalLockLeaseTime, getLockName(threadId));
}

renewExpiration执行流程

renewExpiration() 函数在执行时,会开启一个任务

这个任务会在10s后执行 (internalLockLeaseTime / 3)

10s后执行的这个任务会更新有效期,并“调自己”!

“调自己”说明又会建立这个任务,而这个任务又在10s后执行.......

这也是为什么在oldEntry中不会调这个函数,而新的entry需要调的原因!!!

因为在oldEntry中本身就有这个任务(之前调过,当它刚刚成为entry的时候) ,最后,在释放锁的时候将这个定时任务清除(通过cancelExpirationRenewal()清除任务)


五、Redisson实现主从一致性问题

1、问题描述

为了Redis的可靠性,我们一般会使用Redis的主从模式。

使用了主从模式,一般会采用读写分离的策略,主节点写,从节点读!

那么,当数据被写入主节点的时候,主节点时需要向从节点去同步数据的!

这个过程一定会有延时,一致性问题也就发生在这里!

假如,在主节点中获取到了锁,在主节点向从节点同步这个锁信息的时候,主节点宕机了!那么从节点就会从中挑选一个作为主节点!

可是,此时之前的锁信息就丢失了!也就发生了锁失效的问题!!!

2、Redisson的解决方案——MultiLock

之前我们分析了,主从模式是导致锁失效的原因,所以Redisson中就直接将它们视为相同的角色!

此时,我们获取锁的方式就变了,获取锁的时候,我们需要依次向全部节点获取锁,只有都获取成功时才算成功!!!

如果此时也发生了刚刚描述的问题,是不会出现锁失效的问题的!

分析如下

这套方案就是Redisson中的联锁——MultiLock

官网对于MultiLock的描述
分布式锁和同步器https://www.bookstack.cn/read/redisson-doc-cn/distributed_locks.md#83-multilock

代码实现
配置Redis连接的客户端——3个Client

@Configuration
public class RedisConfig {@Beanpublic RedissonClient redissonClient() {// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.89.128:6379").setPassword("888888");// 创建 RedissonClient 对象return Redisson.create(config);}@Beanpublic RedissonClient redissonClient2() {// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.89.128:6380");// 创建 RedissonClient 对象return Redisson.create(config);}@Beanpublic RedissonClient redissonClient3() {// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.89.128:6381");// 创建 RedissonClient 对象return Redisson.create(config);}
}

联锁的使用

其实和我们之前的代码没有什么差别!

@SpringBootTest
@Slf4j
public class RedissonTest {@Resourceprivate RedissonClient redissonClient;@Resourceprivate RedissonClient redissonClient2;@Resourceprivate RedissonClient redissonClient3;private RLock lock;@BeforeEachvoid setUp() {RLock lock1 = redissonClient.getLock("order");RLock lock2 = redissonClient.getLock("order");RLock lock3 = redissonClient.getLock("order");// 创建联锁lock = redissonClient.getMultiLock(lock1, lock2, lock3);}@Testvoid method1() throws InterruptedException {boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS);if (!isLock) {log.error("获取锁失败 ... 1");return;}try {log.info("获取锁成功 ... 1");method2();log.info("开始执行业务 ... 1");} finally {log.warn("准备释放锁 ... 1");lock.unlock();}}@Testvoid method2() {boolean isLock = lock.tryLock();if (!isLock) {log.error("获取锁失败 ... 2");return;}try {log.info("获取锁成功 ... 2");log.info("开始执行业务 ... 2");} finally {log.warn("准备释放锁 ... 2");lock.unlock();}}
}

为什么使用redissonClient.getMultiLock(...)就行?

我们可以跟一下代码看看!

@Override
public RLock getMultiLock(RLock... locks) {return new RedissonMultiLock(locks);
}

在这里就可以发生,不管是哪一个对象来调,其实都是一样的,这里面其实是在new一个对象RedissonMultiLock() ,所以谁去调getMultiLock()都是一样的!!!

final List<RLock> locks = new ArrayList<>();public RedissonMultiLock(RLock... locks) {if (locks.length == 0) {throw new IllegalArgumentException("Lock objects are not defined");}this.locks.addAll(Arrays.asList(locks));
}

在这里可以发现,这个可变参数被视为集合,然后都添加到数组(集合)里面去了!

所以,按照联锁的原理,在获取锁的时候,也会依次把集合中的每一个锁都获取一次

MultiLock源码分析 !!!

我们这里跟一下tryLock的源码(RedissonMultiLock

@Override
public boolean tryLock(long waitTime, TimeUnit unit) throws InterruptedException {return tryLock(waitTime, -1, unit);
}
// 这里的tryLock() 是RedissonMultiLock的
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long newLeaseTime = -1;if (leaseTime != -1) {// waitTime 为 -1,表示不重试if (waitTime == -1) {newLeaseTime = unit.toMillis(leaseTime);} else {// 如果重试就会对时间做个扩容(放弃waitTime,使用newLeaseTime!)newLeaseTime = unit.toMillis(waitTime)*2;  }}long time = System.currentTimeMillis();// 剩余时间long remainTime = -1;if (waitTime != -1) {remainTime = unit.toMillis(waitTime);}// 锁等待时间 与 剩余时间是一样的!long lockWaitTime = calcLockWaitTime(remainTime);int failedLocksLimit = failedLocksLimit();// 定义一个获取锁成功的集合,初始化肯定是0List<RLock> acquiredLocks = new ArrayList<>(locks.size());for (ListIterator<RLock> iterator = locks.listIterator(); iterator.hasNext();) {RLock lock = iterator.next();boolean lockAcquired;try {if (waitTime == -1 && leaseTime == -1) {// 不重试lockAcquired = lock.tryLock();} else {long awaitTime = Math.min(lockWaitTime, remainTime);// 重试lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);}} catch (RedisResponseTimeoutException e) {unlockInner(Arrays.asList(lock));lockAcquired = false;} catch (Exception e) {lockAcquired = false;}// 判断获取锁是否成功if (lockAcquired) {// 成功,加入集合中acquiredLocks.add(lock);} else {  // 失败// 锁的数量 - 已获取的数量 如果为0,直接breakif (locks.size() - acquiredLocks.size() == failedLocksLimit()) {break;}if (failedLocksLimit == 0) {// 释放已获取到的锁unlockInner(acquiredLocks);// 失败后是否要重试if (waitTime == -1) {return false;}failedLocksLimit = failedLocksLimit();// 把整个获取到的锁集合清空acquiredLocks.clear();// 将指针移到第一个while (iterator.hasPrevious()) {iterator.previous();}} else {failedLocksLimit--;}}// 时间是否耗尽?if (remainTime != -1) {// 用当前时间 - 获取锁开始的时间 = 获取锁的耗时// 剩余时间 - 耗时 = 现在的剩余时间remainTime -= System.currentTimeMillis() - time;time = System.currentTimeMillis();// 判断是否还有时间if (remainTime <= 0) {// 解锁unlockInner(acquiredLocks);return false;}}}// 当leaseTime 为 -1 ,会触发看门狗机制,锁会自动续有效期// 自定义锁的释放时间(建议不要设置!)if (leaseTime != -1) {List<RFuture<Boolean>> futures = new ArrayList<>(acquiredLocks.size());// 遍历拿到的每一把锁,并给它们重新设置有效期for (RLock rLock : acquiredLocks) {RFuture<Boolean> future = ((RedissonLock) rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);futures.add(future);}for (RFuture<Boolean> rFuture : futures) {rFuture.syncUninterruptibly();}}return true;
}

关于Redisson分布式锁的可重入、重试、联锁问题的总结

分布式锁工具之Redisson相关推荐

  1. 分布式锁工具 Redisson,贼香!!

    一.Redisson概述 二.分布式锁 三.Redisson分布式锁 四.RLock 五.公平锁 六.总结 一.Redisson概述 什么是Redisson? Redisson是一个在Redis的基础 ...

  2. 聊聊分布式锁——Redis和Redisson的方式

    聊聊分布式锁--Redis和Redisson的方式 一.什么是分布式锁 分布式~~锁,要这么念,首先得是『分布式』,然后才是『锁』 分布式:这里的分布式指的是分布式系统,涉及到好多技术和理论,包括CA ...

  3. 第六章 商品详情进阶 + redis分布式锁 + redis问题解决 + redisson + 布隆过滤器

    一.商品详情页面优化 1.1 思路 虽然咱们实现了页面需要的功能,但是考虑到该页面是被用户高频访问的,所以性能需要优化. 一般一个系统最大的性能瓶颈,就是数据库的io操作.从数据库入手也是调优性价比最 ...

  4. java分布式锁工具类_java 通过redis实现分布式锁

    1. 开局 在多线程环境中,经常会碰到需要加锁的情况,由于现在的系统基本都是集群分布式部署,JVM的lock已经不能满足分布式要求,分布式锁就这样产生了... 百度一下,网上有很多分布式锁的方案或者例 ...

  5. springboot分布式锁实现(Redisson)

    控制定时任务的执行 浪费资源,想象 10000 台服务器同时执行定时任务 脏数据,比如重复插入数据 要控制定时任务在同一时间只有 1 个服务器能执行. 怎么做? 分离定时任务程序和主程序,只在 1 个 ...

  6. RedissonDistributedLock(redis分布式锁工具类,笔记)

    redisson分布式锁, 支持全局与局部锁 RedissonDistributedLock package com.llb.cloudyoung.tool.lock;import com.llb.c ...

  7. redis分布式锁工具类

    import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.redis.core. ...

  8. redisson的锁的类型_绝对干货:利用redisson完成分布式锁功能

    在单体架构中,我们使用synchronize或者Lock就能完成上锁同步的操作,但是这些在分布式,微服务的今天,失去了作用. 分布式锁的实现一般有三种解决方案:基于数据库表实现 基于缓存实现,比如re ...

  9. 分布式锁中的王者方案:Redisson

    我们先来看下 Redis 官网对分布式锁的说法: 而 Java 版的 分布式锁的框架就是 Redisson. 本篇实战内容将会基于我的开源项目 PassJava 来整合 Redisson. 我把后端. ...

最新文章

  1. PCL中3D特征描述子Shot详解
  2. Java中String、StringBuffer、StringBuilder的区别
  3. sqlserver安装目录_Jira试用报告(安装及数据迁移篇)
  4. 从地址栏中敲入一个网址,到网页显示出来,这个过程经历了什么
  5. 零基础学python数据分析_Python学习指南:使用Python学习数据分析
  6. 5G完整标准出台,产业应用爆发在即,抢先规划势在必行
  7. virtualBox文件共享
  8. 【matlab】直方图(hist函数的应用)
  9. Spark集群 + Akka + Kafka + Scala 开发(2) : 开发一个Spark应用
  10. Android权限管理原理
  11. 最新的物联网10大深度技术,主要包括哪些技术?
  12. 里去频闪的插件叫什么_冬天去海南,你才会发现什么叫“不枉此生”!
  13. GoogLeNet网络结构学习
  14. 电容器功率密度和能量密度
  15. JS中[].slice.call的理解
  16. 高价值行为用户一键同步,精准投放提升广告效率
  17. ASP.Repeater控件使用
  18. 小米全面屏手机浏览器的input、textarea吸底被遮挡
  19. excel怎么设置自动计算_110个算量软件+80个excel自动计算表,提升效率不加班
  20. LA 1010 逻辑分析仪使用方法及解析i2c数据总结

热门文章

  1. STM32开发--4G模块开发
  2. 【对讲机的那点事】你了解对讲机锂电池电路保护吗?
  3. 用最简单的原理做最不简单的事——电磁流量计
  4. VVC学习之二:VTM中CU划分结构QTMTT(3):打印QTMTT最终划分
  5. java 枚举和注解、异常
  6. 科普 | ETH2 Staking 指南 :客户端多样性为何如此重要
  7. Onvif协议客户端开发(1)--Onvif协议介绍
  8. 关于360DOC(个人图书馆)不能复制…
  9. DELL服务器远程控制组件BMC设置及应用
  10. 华为手机打开调试模式adb连接电脑的方法