一、概述

二、使用

2.1 入门

2.1.1 引入依赖

  1. <dependency>
  2. <groupId>org.redisson</groupId>
  3. <artifactId>redisson</artifactId>
  4. <version>3.12.0</version>
  5. </dependency>

2.1.2 配置

首先在配置文件中做好redis相关的配置:

  1. spring:
  2. redis:
  3. host:
  4. port: 6379
  5. password:

然后创建配置类,按照如下方式配置好Redisson客户端Bean对象:

  1. @Configuration
  2. public class MyRedissonConfig {
  3. @Value("${spring.redis.host}")
  4. private String redisHost;
  5. @Value("${spring.redis.port}")
  6. private String redisPort;
  7. @Value("${spring.redis.password}")
  8. private String redisPassword;
  9. /**
  10. * 所有对Redisson的使用都是通过RedissonClient
  11. * @return
  12. * @throws IOException
  13. */
  14. @Bean(destroyMethod="shutdown")
  15. public RedissonClient redisson() throws IOException {
  16. //1、创建配置
  17. Config config = new Config();
  18. config.useSingleServer().setAddress("redis://"+ redisHost + ":" + redisPort);
  19. config.useSingleServer().setPassword(redisPassword);
  20. //2、根据Config创建出RedissonClient实例
  21. //Redis url should start with redis:// or rediss://
  22. RedissonClient redissonClient = Redisson.create(config);
  23. return redissonClient;
  24. }
  25. }

2.1.2 测试

创建测试类如下,测试客户端是否创建成功:

  1. @Slf4j
  2. @RunWith(SpringRunner.class)
  3. @SpringBootTest
  4. public class GulimallProductApplicationTests {
  5. @Autowired
  6. private RedissonClient redissonClient;
  7. @Test
  8. public void testRedisson() {
  9. System.out.println(redissonClient);
  10. }
  11. }

2.2 分布式锁

2.2.1 可重入锁

基于Redis的Redisson分布式可重入锁RLockJava对象实现了java.util.concurrent.locks.Lock接口。

1. 简单使用

  1. @ResponseBody
  2. @GetMapping(value = "/hello")
  3. public String hello() {
  4. //1、获取一把锁,只要锁的名字一样,就是同一把锁
  5. RLock myLock = redisson.getLock("my-lock");
  6. //2、加锁
  7. myLock.lock(); //阻塞式等待。默认加的锁都是30s
  8. try {
  9. System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
  10. } catch (Exception ex) {
  11. ex.printStackTrace();
  12. } finally {
  13. //3、解锁 假设解锁代码没有运行,Redisson会不会出现死锁
  14. System.out.println("释放锁..." + Thread.currentThread().getId());
  15. myLock.unlock();
  16. }
  17. return "hello";
  18. }
  1. <br />在Redisson使用过程中,即使解锁操作没有被正确执行,锁也会在一定时间后被释放掉。锁默认会在30s内自动过期,不会产生死锁问题。默认情况下,如果业务代码没有执行完成,Redisson会通过看门狗机制,运行期间自动锁上新的30s,不用担心业务时间长,锁自动过期被删掉。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22365594/1649139323318-7bd156f1-a2a0-40db-b433-83bc6f1cc64f.png#clientId=u9cf3fa5d-9125-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=393&id=u19d66445&margin=%5Bobject%20Object%5D&name=image.png&originHeight=393&originWidth=1482&originalType=binary&ratio=1&rotation=0&showTitle=false&size=53698&status=done&style=none&taskId=ud9f0c090-d195-4bf9-b59b-a209af2898b&title=&width=1482)<br />如果我们在调用加锁方法时指定了锁的过期时间,这时候看门狗机制则会失效,锁在到期之后自动解锁,此时若执行解锁操作,若存在其他线程已重新获取了锁,则会报错,提示解锁失败。<br />![image.png](https://cdn.nlark.com/yuque/0/2022/png/22365594/1649140161300-c55bfa21-99a4-454b-8920-b1627b7bb732.png#clientId=u9cf3fa5d-9125-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=154&id=ue8074b43&margin=%5Bobject%20Object%5D&name=image.png&originHeight=154&originWidth=1165&originalType=binary&ratio=1&rotation=0&showTitle=false&size=150780&status=done&style=none&taskId=u91f2e751-1a12-456a-b8eb-aee29df21d3&title=&width=1165)

自动续期操作借助于定时任务,每隔指定的时间来扫描业务是否执行完成,如果没有执行完成就执行续期操作,定时任务逻辑如下,可以看到是三分之一的看门狗时间定时执行:
image.png

2.2.2 读写锁

基于Redis的Redisson分布式可重入读写锁RReadWriteLock,Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。
分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。

  1. /**
  2. * 保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁
  3. * 写锁没释放读锁必须等待
  4. * 只要有读或者写的存都必须等待
  5. * @return
  6. */
  7. @GetMapping(value = "/write")
  8. @ResponseBody
  9. public String writeValue() {
  10. String s = "";
  11. RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
  12. RLock rLock = readWriteLock.writeLock();
  13. try {
  14. //1、改数据加写锁,读数据加读锁
  15. rLock.lock();
  16. s = UUID.randomUUID().toString();
  17. ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
  18. ops.set("writeValue",s);
  19. TimeUnit.SECONDS.sleep(10);
  20. } catch (InterruptedException e) {
  21. e.printStackTrace();
  22. } finally {
  23. rLock.unlock();
  24. }
  25. return s;
  26. }
  1. @GetMapping(value = "/read")
  2. @ResponseBody
  3. public String readValue() {
  4. String s = "";
  5. RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");
  6. //加读锁
  7. RLock rLock = readWriteLock.readLock();
  8. try {
  9. rLock.lock();
  10. ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
  11. s = ops.get("writeValue");
  12. try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. } finally {
  16. rLock.unlock();
  17. }
  18. return s;
  19. }
  • 读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
  • 写 + 读 :必须等待写锁释放
  • 写 + 写 :阻塞方式
  • 读 + 写 :有读锁。写也需要等待

2.2.3 信号量

基于Redis的Redisson的分布式信号量Semaphore,Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。

  1. /**
  2. * 车库停车
  3. * 3车位
  4. * 信号量也可以做分布式限流
  5. */
  6. @GetMapping(value = "/park")
  7. @ResponseBody
  8. public String park() throws InterruptedException {
  9. RSemaphore park = redisson.getSemaphore("park");
  10. park.acquire(); //获取一个信号、获取一个值,占一个车位
  11. boolean flag = park.tryAcquire();
  12. if (flag) {
  13. //执行业务
  14. } else {
  15. return "error";
  16. }
  17. return "ok=>" + flag;
  18. }
  1. @GetMapping(value = "/go")
  2. @ResponseBody
  3. public String go() {
  4. RSemaphore park = redisson.getSemaphore("park");
  5. park.release(); //释放一个车位
  6. return "ok";
  7. }

2.2.4 闭锁

基于Redisson的Redisson分布式闭锁CountDownLatch,Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。

  1. /**
  2. * 放假、锁门
  3. * 1班没人了
  4. * 5个班,全部走完,我们才可以锁大门
  5. * 分布式闭锁
  6. */
  7. @GetMapping(value = "/lockDoor")
  8. @ResponseBody
  9. public String lockDoor() throws InterruptedException {
  10. RCountDownLatch door = redisson.getCountDownLatch("door");
  11. door.trySetCount(5);
  12. door.await(); //等待闭锁完成
  13. return "放假了...";
  14. }
  1. @GetMapping(value = "/gogogo/{id}")
  2. @ResponseBody
  3. public String gogogo(@PathVariable("id") Long id) {
  4. RCountDownLatch door = redisson.getCountDownLatch("door");
  5. door.countDown(); //计数-1
  6. return id + "班的人都走了...";
  7. }