当前位置:   article > 正文

Redisson实现分布式锁_redisson 分布式锁

redisson 分布式锁

一、Redission在SpringBoot中的集成

1.导入依赖

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

2.创建Redis客户端

  1. @Configuration
  2. public class RedissonConfig {
  3. @Bean
  4. public RedissonClient redissonClient(){
  5. Config config = new Config();
  6. config.useSingleServer().setAddress("redis://127.0.0.1:6379");//.setPassword("123456");
  7. return Redisson.create(config);
  8. }
  9. }

3.实现分布式锁

  1. @Autowired
  2. private RedissonClient redissonClient;
  3. @Test
  4. public void testLock(){
  5. RLock rLock = redissonClient.getLock("lock_stock");
  6. //阻塞式等待,过期时间30s
  7. rLock.lock();
  8. try{
  9. //执行业务
  10. }finally {
  11. //释放锁
  12. rLock.unlock();
  13. }
  14. }

二、Redission实现分布式锁底层原理

        如果负责存储分布式锁的Redission节点发生了宕机,并且锁没有被释放,就会出现死锁的现象。因此,为防止这种现象的产生,Redission有一个看门狗的机制,用来监控锁,若Redission实例未关闭,就会不断的延长锁的有效期,防止程序执行期间自动过期删除的问题。

        Redisson加锁有以下两种情况:

        (1)设置了过期时间

          若设置了过期时间,会把设定的时间作为过期时间,然后使用Lua脚本获取到锁,未获取到锁的线程则会自旋重入,不停地尝试获取锁。

          如果我们通过lease Time(rLock.lock(20, TimeUnit.SECONDS))的参数指定了加锁的时间,那么Redisson就不会再进行续期了,锁到达过期时间会自动释放锁,无需unlock手动解锁,如果出现锁的误删除情况时,Redisson会抛出异常。

        (2)未设置过期时间

        未设置过期时间的,Redisson加锁会有一个默认的过期时间为30s,线程获取到锁后,用了Lua来保证原子性,会开启一个定时任务,每隔10s看门狗会执行一次定时任务,若锁还在,重新将过期时间设置成30s。

三、Redisson锁的分类

1.可重入锁

  1. @Autowired
  2. private RedissonClient redissonClient;
  3. @Test
  4. public void testLock(){
  5. //获取锁
  6. RLock rLock = redissonClient.getLock("lock_stock");
  7. //加锁,锁的过期时间是10s,10s后锁自动释放
  8. rLock.lock(10, TimeUnit.SECONDS);
  9. try{
  10. //执行业务
  11. }finally {
  12. //释放锁,设置了锁的过期时间,不会手动调这个方法释放锁
  13. rLock.unlock();
  14. }
  15. }

2.公平锁

        公平锁保证了当多个Redisson客户端线程同时请求加锁时,会按照请求的顺序进行加锁,遵循先到先得的原则。

  1. @Autowired
  2. private RedissonClient redissonClient;
  3. @Test
  4. public void testLock5() {
  5. RLock fairLock = redissonClient.getFairLock("anyLock");
  6. try{
  7. fairLock.lock();
  8. }finally {
  9. //释放锁
  10. fairLock.unlock();
  11. }
  12. }

3.联锁

        通过RedissonMultiLock对象可以将多个RLock锁关联为一个联锁,其中每个RLock对象实例都可以来自不同的Redisson实例。

  1. RLock lockA = redissonInstanceA.getLock("lockA");
  2. RLock lockB = redissonInstanceB.getLock("lockB");
  3. RLock lockC = redissonInstanceC.getLock("lockC");
  4. // 同时加锁:lockA, lockB, lockC
  5. RedissonMultiLock lock = new RedissonMultiLock(lockA, lockB, lockC);
  6. // 所有的都加锁成功才是成功
  7. lock.lock();
  8. // 释放锁
  9. lock.unlock();

4.红锁

        可以将多个RLock关联成一个红锁,每个RLock对象实例可以来自于不同的Redisson实例,RedLock可以保证以下特性:

        (1)容错性:只要大多数节点的redis实例能正常运行就可以加锁和释放锁。

        (2)互斥性:只有一个客户端可以获取到锁,即使发生宕机,也不会造成死锁。

        (3)一致性:降低了数据不一致的可能,但不能完全保证数据的一致性。

  1. RLock lockA = redissonInstanceA.getLock("lockA");
  2. RLock lockB = redissonInstanceB.getLock("lockB");
  3. RLock lockC = redissonInstanceC.getLock("lockC");
  4. // 同时加锁:lockA, lockB, lockC
  5. RedissonRedLock = new RedissonRedLock(lockA, lockB, lockC);
  6. // 大部分节点加锁成功就算成功
  7. lock.lock();
  8. // 释放锁
  9. lock.unlock();

5.闭锁

        Redisson提供了RCountDownLatch接口,它是一个计数降低到0时触发的锁。它可以实现在多个线程都执行完才结束的效果,否则就会闭锁来等待。

  1. RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
  2. //设置数量为2
  3. latch.trySetCount(2);
  4. //await方法等待其他线程完成所有的trySetCount(2)就会结束闭锁
  5. latch.await();
  6. //在其他线程或其他JVM里
  7. RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
  8. //完成第1个countDown
  9. latch.countDown();
  10. // 在其他线程或其他JVM里
  11. RCountDownLatch latch = redisson.getCountDownLatch("anyCountDownLatch");
  12. //完成第2个countDown,闭锁完成
  13. latch.countDown();

6.读写锁

        Redisson的读写锁RReadWriteLock Java对象实现了ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。

        分布式的读写锁允许同时有多个读锁和一个写锁处在加锁的状态,也就是使用同一个RReadWriteLock来加写锁和读锁,读锁需要等待写锁释放锁之后才能够加锁成功。

  1. @Autowired
  2. private RedissonClient redissonClient;
  3. @Test
  4. public void testWriteLock() {
  5. //获取读写锁
  6. RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ReadWriteLock");
  7. //获取写锁
  8. RLock rLock = readWriteLock.writeLock();
  9. try{
  10. //加写锁,读等待
  11. rLock.lock();
  12. Thread.sleep(200000);
  13. //执行写业务
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }finally {
  17. rLock.unlock();
  18. }
  19. }
  20. @Test
  21. public void testReadLock() {
  22. //获取读写锁
  23. RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("ReadWriteLock");
  24. //获取读锁
  25. RLock rLock = readWriteLock.readLock();
  26. try{
  27. //加上读锁,若写锁没释放,等待
  28. rLock.lock();
  29. //处理读业务
  30. }finally {
  31. rLock.unlock();
  32. }
  33. }

7.信号量

        Redisson的信号量可以看做是在Redis中保存了一个数字,可以实现原子性的加或减,比如取10件商品做库存,就可以把这个库存做成信号量,然后实现原子性加减。防止了库存出现负数的情况。

  1. @Autowired
  2. private RedissonClient redissonClient;
  3. @Test
  4. public void testReadLock() throws InterruptedException {
  5. //获得1个信号量
  6. RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
  7. //设置信号量的值
  8. boolean setPermits = semaphore.trySetPermits(1000);
  9. }
  10. @Test
  11. public void testReadLock6() throws InterruptedException {
  12. //获得到1个信号量
  13. RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
  14. //获取2个信号量 , 值会减去2 , 若获取不到,方法会阻塞
  15. semaphore.acquire(2);
  16. //尝试获取2个信号量 , 值会减去2 , 若获取不到,方法不会阻塞
  17. boolean tryAccquireSuccess = semaphore.tryAcquire(2);
  18. }
  19. @Test
  20. public void testReadLock7() throws InterruptedException {
  21. //获得到1个信号量
  22. RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
  23. //释放2个值,数量会加回去
  24. semaphore.release(2);
  25. }

        除了上述的信号量,还提供了一个可过期的信号量,在RSemaphore对象的基础上,为每个信号增加了一个过期时间。每个信号可通过ID来进行区分,释放时也只能通过提交的ID才能释放。

  1. RPermitExpirableSemaphore semaphore = redisson.getPermitExpirableSemaphore("mySemaphore");
  2. String id = semaphore.acquire();
  3. // 获取1个信号,有效期只有1秒钟。
  4. String id = semaphore.acquire(1, TimeUnit.SECONDS);
  5. semaphore.release(id);

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号