大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
可重入锁,是单体Redis推荐的,(集群式的Redis则推荐使用红锁),简单来说便是在获取到锁之后,调用一个新的线程去轮询(轮询间隔大概是锁超时时间的1/3)的查看是否已到超时时间,到了而线程还没执行完,就给这个锁续上时间以便它能拿到锁继续执行,保证不被别的线程抢走。
要使用Redisson,就当然要引入依赖,在spring-boot-starter中,也有相关的依赖,直接引入如下依赖就可以了。
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>${redisson.version}</version>
</dependency>
作为演示,只使用最简单的单机式Redis,所以就添加一个Redisson的配置类就可以了。注入一个RedissonClient的bean,填好相关的地址和密码就可以了。
@Bean
public RedissonClient redisson() throws IOException {
Config config = new Config();
config.useSingleServer().setAddress("redis://"+url + ":" + port).setPassword(password);
return Redisson.create(config);
}
最后,再对我们的服务进行一些小的修改,把原先我们自己实现的分布式锁给替换成Redisson实现的分布式锁。
//标识锁用
String lock = "phoneLock";
//获取锁
RLock rLock = redissonClient.getLock(lock);
if (rLock.isLocked()) {
System.out.println("活动太火爆了,请稍后再试!");
return "活动太火爆了,请稍后再试!";
}
//获取到了,才需要finally去释放锁
try {
//加锁
rLock.lock(5, TimeUnit.SECONDS);
//取出phone的剩余数量
String phone = redisTemplate.opsForValue().get("phone");
if (phone == null) {
System.out.println("没抢到...");
return "没抢到...";
}
int number = Integer.parseInt(phone);
//还有剩余
if (number > 0) {
//消费一个
int newNumber = number - 1;
//将消费完的phone的数量,重新放到redis中
redisTemplate.opsForValue().set("phone", String.valueOf(newNumber));
System.out.println("恭喜抢到啦!" + newNumber);
return "恭喜抢到啦!";
} else {
System.out.println("没抢到...");
return "没抢到...";
}
} finally {
if (rLock.isLocked()) {
//释放锁
rLock.unlock();
}
}
然后同样的执行一下,输出结果如下。
通过Redisson,我们可以非常方便快捷的使用分布式锁。本项目就演示了简单的可重入锁的使用,不同场景需求Redisson也给我们提供了不同的锁,具体问题,具体分析需要什么锁。
最后再简单介绍一下各种锁吧:
- 可重入锁
基于Redis的Redisson分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。
大家都知道,如果负责储存这个分布式锁的Redisson节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson内部提供了一个监控锁的看门狗,它的作用是在Redisson实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout来另行指定。
另外Redisson还通过加锁的方法提供了leaseTime的参数来指定加锁的时间。超过这个时间后锁便自动解开了。- 公平锁
它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。- 联锁
基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。- 红锁(RedLock)
基于Redis的Redisson红锁RedissonRedLock对象实现了Redlock介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。- 读写锁(ReadWriteLock)
分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。- 信号量(Semaphore)
基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。- 可过期性信号量(PermitExpirableSemaphore)
基于Redis的Redisson可过期性信号量(PermitExpirableSemaphore)是在RSemaphore对象的基础上,为每个信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。它提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。- 闭锁(CountDownLatch)
基于Redisson的Redisson分布式闭锁(CountDownLatch)Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。