文章目录
一、概述
Redisson
是一个在Redis
的基础上实现的Java
驻内存数据网格(In-Memory Data Grid
)。它不仅提供了一系列的分布式的Java
常用对象,还提供了许多分布式服务。Redisson
提供了使用Redis
的最简单和最便捷的方法。Redisson
的宗旨是促进使用者对Redis
的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。
Redisson
和jedis
以及lettuce
一样都是redis
客户端,只不过Redisson
功能更强大。
官方文档:https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
二、Redisson的使用
2.1 搭建环境
① 引入redssion
坐标依赖:
<!-- 以后使用redisson作为所有分布式锁,分布式对象等功能框架-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
② 配置redisson
,程序化的配置方法是通过构建Config
对象实例来实现的:
@Configuration
public class MyRedissonConfig {
//注册RedissonClient对象
@Bean(destroyMethod="shutdown")
RedissonClient redisson() throws IOException {
Config config = new Config();
config.useSingleServer().setAddress("redis://192.168.182.150:6379");
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
}
}
2.2 可重入锁(Reentrant Lock)
@ResponseBody
@GetMapping("/hello")
public String hello(){
//获取一把锁
RLock lock = redissonClient.getLock("my-lock");
//加锁
lock.lock();
//锁的自动续期,如果业务执行时间超长,运行期间会自动给锁续期30秒时间,不用担心业务时间长,锁自动过期
//加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30秒后也会自动删除
try {
System.out.println("加锁成功,执行业务.... "+Thread.currentThread().getId());
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//手动解锁
System.out.println("解锁..."+Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
如果负责储存这个分布式锁的Redisson
节点宕机以后,而且这个锁正好处于锁住的状态时,这个锁会出现锁死的状态。为了避免这种情况的发生,Redisson
内部提供了一个监控锁的看门狗,它的作用是在Redisson
实例被关闭前,不断的延长锁的有效期。默认情况下,看门狗的检查锁的超时时间是30秒钟,也可以通过修改Config.lockWatchdogTimeout
来另行指定。
另外Redisson
还通过加锁的方法提供了leaseTime
的参数来指定加锁的时间。超过这个时间后锁便自动解开了。
@ResponseBody
@GetMapping("/hello")
public String hello(){
//获取一把锁
RLock lock = redissonClient.getLock("my-lock");
// 加锁
// 设置的自动解锁时间一定要大于业务执行时间,因为在锁时间到了以后,不会自动续期
lock.lock(10, TimeUnit.SECONDS);
try {
System.out.println("加锁成功,执行业务.... "+Thread.currentThread().getId());
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//解锁
System.out.println("解锁..."+Thread.currentThread().getId());
lock.unlock();
}
return "hello";
}
结论:
lock.lock()
即没有指定锁的过期时间,就是用30s,即看门狗的默认时间,只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒就会自动续期到30秒。lock.lock(10, TimeUnit.SECONDS)
,默认锁的过期时间就是我们指定的时间。
2.3 读写锁
基于Redis
的Redisson
分布式可重入读写锁RReadWriteLock Java
对象实现了java.util.concurrent.locks.ReadWriteLock
接口。其中读锁和写锁都继承了RLock
接口。
分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
① 读锁
@GetMapping("/read")
public String readValue(){
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
//加读锁
RLock rLock = lock.readLock();
rLock.lock();
try {
System.out.println("读锁加锁成功"+Thread.currentThread().getId());
s = redisTemplate.opsForValue().get("writeValue");
Thread.sleep(30000);
} catch (Exception e) {
e.printStackTrace();
} finally {
rLock.unlock();
System.out.println("读锁释放"+Thread.currentThread().getId());
}
return s;
}
② 写锁
@GetMapping("/write")
public String writeValue(){
// 获取一把锁
RReadWriteLock lock = redissonClient.getReadWriteLock("rw-lock");
String s = "";
// 加写锁
RLock rLock = lock.writeLock();
try {
//1、改数据加写锁,读数据加读锁
rLock.lock();
System.out.println("写锁加锁成功..."+Thread.currentThread().getId());
s = UUID.randomUUID().toString();
Thread.sleep(30000);
redisTemplate.opsForValue().set("writeValue",s);
} catch (Exception e) {
e.printStackTrace();
} finally {
rLock.unlock();
System.out.println("写锁释放"+Thread.currentThread().getId());
}
return s;
}
测试:
- 先加写锁,后加读锁,此时并不会立刻给数据加读锁,而是需要等待写锁释放后,才能加读锁
- 先加读锁,再加写锁:有读锁,写锁需要等待
- 先加读锁,再加读锁:并发读锁相当于无锁模式,会同时加锁成功
总结:只要有写锁的存在,都必须等待,写锁是一个排他锁,只能有一个写锁存在,读锁是一个共享锁,可以有多个读锁同时存在。
2.4 信号量(Semaphore)
基于Redis
的Redisson
的分布式信号量(Semaphore
)Java
对象RSemaphore
采用了与java.util.concurrent.Semaphore
相似的接口和用法
RSemaphore semaphore = redisson.getSemaphore("semaphore");
semaphore.acquire();
//或
semaphore.acquireAsync();
semaphore.acquire(23);
semaphore.tryAcquire();
//或
semaphore.tryAcquireAsync();
semaphore.tryAcquire(23, TimeUnit.SECONDS);
//或
semaphore.tryAcquireAsync(23, TimeUnit.SECONDS);
semaphore.release(10);
semaphore.release();
//或
semaphore.releaseAsync();
2.5 闭锁(CountDownLatch)
基于Redisson
的Redisson
分布式闭锁(CountDownLatch
)Java
对象RCountDownLatch
采用了与java.util.concurrent.CountDownLatch
相似的接口和用法。
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();
// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();