当前位置: 首页 > news >正文

RedLock + Redisson

目录

    • 2.9 RedLock
      • 2.9.1 上述实现的分布式锁在集群状态下失效的原因
      • 2.9.2 解决方式-RedLock
    • 2.10 ==redisson中的分布式锁==
      • 2.10.0 redisson简介以及简单使用
        • 简单使用redisson中的锁
        • Redisson常用配置
      • 2.10.1 Redisson可重入锁实现原理
      • 2.10.2 公平锁(Fair Lock)
      • 2.10.3 联锁(MultiLock) (没用 !!!!)
      • 2.10.4 红锁(RedLock)
      • 2.10.5 读写锁(ReadWriteLock)
      • 2.10.6 ==信号量(Semaphore)==
      • 2.10.7 ==闭锁==(CountDownLatch)

2.9 RedLock

2.9.1 上述实现的分布式锁在集群状态下失效的原因

redis集群状态下的问题:

  1. 客户端A从master获取到锁
  2. 在master将锁同步到slave之前,master宕掉了。
  3. slave节点被晋级为master节点
  4. 客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。

2.9.2 解决方式-RedLock

解决集群下锁失效,参照redis官方网站针对redlock文档:https://redis.io/topics/distlock

加锁算法
在这里插入图片描述

2.10 redisson中的分布式锁

2.10.0 redisson简介以及简单使用

Redisson提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service)

Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

在这里插入图片描述

官方文档地址:https://github.com/redisson/redisson/wiki

简单使用redisson中的锁

注入 RedissonClient 客户端

@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 默认连接地址 127.0.0.1:6379Config config = new Config();config.useSingleServer().setAddress("redis://121.40.182.123:6379").setPassword("houchen");RedissonClient redisson = Redisson.create(config);return redisson;}
}

使用 RedisClient的锁。StockService 代码片段如下

    public void deduct() {RLock lock = redissonClient.getLock("lock");lock.lock();try {String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");// 2. 判断库存是否充足if (stockStr != null && stockStr.length() != 0) {Long stock = Long.parseLong(stockStr);if (stock > 0) {redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));}}} finally {lock.unlock();}}

使用 Jmeter并发压测后,查看redis的库存确实减为0,表明redisson锁是确实是可用的
在这里插入图片描述

Redisson常用配置

在这里插入图片描述

2.10.1 Redisson可重入锁实现原理

之后进行补充

可重入锁演示测试

  public void deduct() {RLock lock = redissonClient.getLock("lock");lock.lock();try {String stockStr = redisTemplate.opsForValue().get("stock:" + "1001");// 2. 判断库存是否充足if (stockStr != null && stockStr.length() != 0) {Long stock = Long.parseLong(stockStr);if (stock > 0) {redisTemplate.opsForValue().set("stock:" + "1001", String.valueOf(stock - 1));}}this.test1();} finally {lock.unlock();}}private void test1() {RLock lock = redissonClient.getLock("lock");lock.lock();System.out.println("获取到可重入锁");System.out.println("执行其他业务代码......");lock.unlock();}

由演示结果可以得出结论,RLock 是可重入锁!!
在这里插入图片描述

2.10.2 公平锁(Fair Lock)

基于Redis的Redisson分布式可重入公平锁也是实现了java.util.concurrent.locks.Lock接口的一种RLock对象。它保证了当多个Redisson客户端线程同时请求加锁时,优先分配给先发出请求的线程。所有请求线程会在一个队列中排队,当某个线程出现宕机时,Redisson会等待5秒后继续下一个线程,也就是说如果前面有5个线程都处于等待状态,那么后面的线程会等待至少25秒。

RLock fairLock = redisson.getFairLock("anyLock");
// 最常见的使用方法
fairLock.lock();// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
fairLock.lock(10, TimeUnit.SECONDS);// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);
fairLock.unlock();

2.10.3 联锁(MultiLock) (没用 !!!!)

基于Redis的Redisson分布式联锁RedissonMultiLock对象可以将多个RLock对象关联为一个联锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 所有的锁都上锁成功才算成功。
lock.lock();
...
lock.unlock();

2.10.4 红锁(RedLock)

基于Redis的Redisson红锁RedissonRedLock对象实现了 2.9 RedLock 介绍的加锁算法。该对象也可以用来将多个RLock对象关联为一个红锁,每个RLock对象实例可以来自于不同的Redisson实例。

RLock lock1 = redissonInstance1.getLock("lock1");
RLock lock2 = redissonInstance2.getLock("lock2");
RLock lock3 = redissonInstance3.getLock("lock3");RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);
// 同时加锁:lock1 lock2 lock3
// 红锁在大部分节点上加锁成功就算成功。
lock.lock();
...
lock.unlock();

2.10.5 读写锁(ReadWriteLock)

基于Redis的Redisson分布式可重入读写锁RReadWriteLock Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。

分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
// 最常见的使用方法
rwlock.readLock().lock();
// 或
rwlock.writeLock().lock();// 10秒钟以后自动解锁
// 无需调用unlock方法手动解锁
rwlock.readLock().lock(10, TimeUnit.SECONDS);
// 或
rwlock.writeLock().lock(10, TimeUnit.SECONDS);// 尝试加锁,最多等待100秒,上锁以后10秒自动解锁
boolean res = rwlock.readLock().tryLock(100, 10, TimeUnit.SECONDS);
// 或
boolean res = rwlock.writeLock().tryLock(100, 10, TimeUnit.SECONDS);
...
lock.unlock();

添加StockController方法:

@GetMapping("test/read")
public String testRead(){String msg = stockService.testRead();return "测试读";
}@GetMapping("test/write")
public String testWrite(){String msg = stockService.testWrite();return "测试写";
}

添加StockService方法:

public String testRead() {RReadWriteLock rwLock = this.redissonClient.getReadWriteLock("rwLock");rwLock.readLock().lock(10, TimeUnit.SECONDS);System.out.println("测试读锁。。。。");// rwLock.readLock().unlock();return null;
}public String testWrite() {RReadWriteLock rwLock = this.redissonClient.getReadWriteLock("rwLock");rwLock.writeLock().lock(10, TimeUnit.SECONDS);System.out.println("测试写锁。。。。");// rwLock.writeLock().unlock();return null;
}
  • 同时访问写:一个写完之后,等待一会儿(约10s),另一个写开始
  • 同时访问读:不用等待
  • 先写后读:读要等待(约10s)写完成
  • 先读后写:写要等待(约10s)读完成

2.10.6 信号量(Semaphore)

JUC Semaphore的小小演示:

 public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);for (int i = 0; i < 5; i++) {new Thread(() -> {try {semaphore.acquire(1);System.out.println(Thread.currentThread().getName() + "获取到停车位");TimeUnit.SECONDS.sleep(new Random().nextInt(10));System.out.println(Thread.currentThread().getName() + "将车开走了!!!");semaphore.release();} catch (InterruptedException e) {throw new RuntimeException(e);}}, i + "号车").start();}}

可以看到任意一个时刻,最多只有三辆车能获取到停车位
在这里插入图片描述

基于Redis的Redisson的分布式信号量(Semaphore)Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。同时还提供了异步(Async)、反射式(Reactive)和RxJava2标准的接口。

在StockController添加方法:

@GetMapping("test/semaphore")
public String testSemaphore(){this.stockService.testSemaphore();return "测试信号量";
}

在StockService添加方法:

public void testSemaphore() {RSemaphore semaphore = this.redissonClient.getSemaphore("semaphore");semaphore.trySetPermits(3);try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + "获取了信号量资源,开始执行业务逻辑,----------> start");TimeUnit.SECONDS.sleep(new Random().nextInt(10));System.out.println(Thread.currentThread().getName() + "获取执行完了业务逻辑,释放信号量资源,----------> end");semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}

在这里插入图片描述

2.10.7 闭锁(CountDownLatch)

JUC CountDownLatch使用demo

CountDownLatch 适用于一个线程等待一组线程的执行完成!!!!

public static void main(String[] args) throws InterruptedException {CountDownLatch countDownLatch= new CountDownLatch(5);for (int i = 0; i < 5; i++) {new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + "---> 准备出门了");TimeUnit.SECONDS.sleep(new Random().nextInt(10));System.out.println(Thread.currentThread().getName() + "---> 真真切切出门了.....");countDownLatch.countDown();} catch (InterruptedException e) {throw new RuntimeException(e);}}, i + "同学").start();}countDownLatch.await();System.out.println("班长关门了...");

在这里插入图片描述

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

RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.trySetCount(1);
latch.await();// 在其他线程或其他JVM里
RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
latch.countDown();

需要两个方法:一个等待,一个计数countDown

给StockController添加测试方法:

@GetMapping("test/latch")
public String testLatch(){this.stockService.testLatch();return "班长锁门。。。";
}@GetMapping("test/countdown")
public String testCountDown(){this.stockService.testCountDown();return "出来了一位同学";
}

给StockService添加测试方法:

public void testLatch() {RCountDownLatch latch = this.redissonClient.getCountDownLatch("latch");latch.trySetCount(6);try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}
}public void testCountDown() {RCountDownLatch latch = this.redissonClient.getCountDownLatch("latch");latch.trySetCount(6);latch.countDown();
}

重启测试,打开两个页面:当第二个请求执行6次之后,第一个请求才会执行。

http://www.lryc.cn/news/103104.html

相关文章:

  • 计算机视觉:卷积层的参数量是多少?
  • Docker 容器基础操作
  • 【Vue3+Ts+Vite】配置滚动条样式
  • react map使用方法详解
  • PoseiSwap:通过 RWA 的全新叙事,反哺 Nautilus Chain 生态
  • PHP8的常量-PHP8知识详解
  • 印花税的本质和底层逻辑是什么?印花税降低是利好股市吗?
  • pytorch+GPU跑模型时 nvrtc: error: failed to open nvrtc-builtins64_117.dll
  • SQL-每日一题【626.换座位】
  • Swagger之Hello World !
  • VSCode SSH远程连接与删除
  • 面试典中典之线程池的七大参数
  • Maven如何创建Java web项目(纯干货版)!!!
  • linux进程间通信的方式及特点
  • 京东的成功秘诀:找到自己独特而有效的商业模式
  • 全局ip代理安全吗? 手机设置全局代理方法详解
  • Clion开发Stm32之温湿度传感器(DHT11)驱动编写
  • 位操作相关的函数(C++)
  • arm 函数栈回溯
  • 30个前端开发中常用的JavaScript函数
  • 基于量子同态加密的改进多方量子私有比较
  • 解决mysqld服务启动失败
  • 【前端知识】React 基础巩固(四十)——Navigate导航
  • 文件IO练习
  • 初识FreeRTOS入门,对FreeRTOS简介、任务调度、内存管理、通信机制以及IO操作,控制两个led不同频率闪烁
  • STM32CUBUMX配置FLASH(W25Q128)--保姆级教程
  • 【Golang 接口自动化04】 解析接口返回JSON串
  • EPPlus与Microsoft.Office.Interop.Excel的使用区别
  • ncrack工具使用说明
  • 第二章:进程管理(处理机/CPU管理)