springboot整合redisson实现多种分布式锁

Redisson概述

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的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),从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

一句话说Redisson就是redis的java客户端,底层通信使用Netty,提供了众多redis场景的API,比如分布式锁等等。

Springboot整合redisson:

这里整合方式不是重点,有多种整合方式和模式。
有配置文件Yml整合,编程式方法整合。

有整合单节点redis的方法、整合哨兵模式的方法、整合集群方法。
这里就整合单节点redis的方法。其他方法可以看GitHub的文档,有中文的。
https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95

引入依赖:

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.13.3</version>
</dependency>

@Configuration
public class RedissonConfig {


    @Bean
    public RedissonClient redissonClient(){
        Config config = new Config();
        //配置redis的服务地址,地址要以redis://ip:port  如果是安全连接ssl,就要rediss://ip:port
        config.useSingleServer().setAddress("redis://192.168.18.140:6379");
        //创建一个RedissonClient,redisson的主要操作都是通过这个客户端完成。
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

//整合完成
可重入锁
@Test
	void testRedissonReentrantLock(){
		//获取RLock对象,输入锁的名字,只有相同名字的锁才是同一把锁。
		RLock rLock = redissonClient.getLock("redissonReentrantLock");
		//上锁,该方法会阻塞当前线程,直到获取锁才往下走。
		rLock.lock();

		//下面方法执行时设置锁的超时时间为十秒,是另一个上锁重载方法,该方法会跳过看门狗
		//机制,即使锁超时后业务代码没有执行完成,依旧会释放锁,而不会对锁进行续期。
		rLock.lock(10,TimeUnit.SECONDS);
		try {
			System.out.println("获取分布式可重入锁成功");
			//执行业务代码
			try {
				TimeUnit.SECONDS.sleep(40);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}finally {
			//释放锁
			rLock.unlock();
		}


	}

@Test
	void testRedissonReentrantTryLock(){
		//获取RLock对象,输入锁的名字,只有相同名字的锁才是同一把锁。
		RLock rLock = redissonClient.getLock("redissonReentrantLock");
		boolean succeed = false;
		try {
			//尝试获取锁,第一个参数是等待时间,第二个参数是锁的超时时间,
			//该方法尝试获取锁,不一定要获取成功,设置一个等待时间,线程会最多阻塞住该时间
			//假如在等待时间内还是没有获取锁的话,线程就会不阻塞了,继续往下走,只是没有获取到
			//锁succeed 会为false。如果获取到了锁,succeed会为true。
			//如果在等待时间内获取到了锁,线程就会往下执行。
			succeed = rLock.tryLock(100, 10, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (succeed){
			try {
				System.out.println("获取分布式可重入锁成功");
				//执行业务代码
				try {
					TimeUnit.SECONDS.sleep(40);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}finally {
				//释放锁
				rLock.unlock();
			}
		}


	}
看门狗机制

默认的锁超时时间是30秒超时,但是Redisson提供了一个看门狗机制,锁的默认超时时间就是这个看门狗时间,默认30秒,可以通过Config.lockWatchdogTimeout来指定。
在这里插入图片描述

看门狗机制流程是这样的:

  1. 线程获取分布式锁,锁的默认超时时间是30秒。
  2. 但是有可能业务代码比这个锁的时间要长,为了避免业务代码没执行完成锁释放了,看门狗机制默认会在看门狗时间的三分之一时对锁的时间进行重置,比如看门狗时间是30秒,业务代码执行50秒,那么看门狗机制就每个10秒对锁进行一次超时重置回30秒,直到业务代码执行完成释放锁为止。
  3. Redisson连接出现异常断开后,看门狗机制也就失效了,那么锁会在超时后释放,不再进行重置。
公平锁

公平锁是指获取锁的顺序是按照请求的发起顺序决定的,公平的,类似把获取锁的线程放进一个先进先出的队列,当锁释放后,由队列弹出一个线程来获取成功,获取的线程是有序的。
而非公平锁是一旦锁释放了,所以的线程都会被唤醒来争抢锁。

使用方式:

//使用getFairLock方法获取,剩下的操作方式跟上面的可重入锁一样。
RLock rLock = redissonClient.getFairLock("redissonReentrantFairLock");
读写锁

读写锁的特点:
读锁又称共享锁,多个读锁可以共同访问同一资源。
写锁又称排他锁,某资源被上锁后,其他线程(进程)就不能访问该资源,直到释放锁。

读写锁总是成对出现的,确保读锁读到的数据时最新的。

  • 获取到读锁后,其他读锁可以访问该资源,写锁不能访问该资源。
  • 获取到写锁后,其他读写锁都不能访问该资源。

读写锁的原理实现在博主其他文章里有介绍,不过是使用Zookeeper实现的。

@Test
	//读锁
	public void testReadLock(){
		//获取读写锁RReadWriteLock ,读锁跟写锁是成对出现的,锁定的资源也是一样的,所以锁的名称要一样
		RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("r-w-lock");
		//获取一把读锁
		RLock readLock = readWriteLock.readLock();
		//其余操作就跟可重入锁一样了
		readLock.lock();
		try {
			System.out.println("获取了读锁");
			try {
				TimeUnit.SECONDS.sleep(30);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}finally {
		//释放锁
			readLock.unlock();
		}
	}


	@Test
	//写锁
	public void testWriteLock(){
	//锁名要与读锁一致,因为是成对出现
		RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("r-w-lock");
		RLock writeLock = readWriteLock.writeLock();
		writeLock.lock();
		try {
			System.out.println("获取了写锁");
			try {
				TimeUnit.SECONDS.sleep(30);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}finally {
			writeLock.unlock();
		}
	}

可以自己测试,当读锁状态时,其他读线程可以进入访问,写线程不可以,当写锁状态时,读写锁都不能进入。

闭锁

Redisson提供了分布式闭锁的API,与JUC的本地闭锁CountdownLatch提供的功能类似。但是他是分布式的闭锁。

@Test
	//上锁
	public void testCountDownLatchLock(){
		RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch("countDownLatch");
		//设置计数器为10,当减到0时取消阻塞,执行下面代码。
		rCountDownLatch.trySetCount(10);
		try {
			rCountDownLatch.await();
			//执行业务流程
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	@Test
	public void testCountDownLatchLockRelease(){
		RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch("countDownLatch");
		//减一
		rCountDownLatch.countDown();
	}
信号量

信号量就类似于一个信号数量。信号数量有剩时,就能获取到信号来执行下面的操作。
比如一个信号量有5个信号,就类比停车场有5个车位,信号量就是停车场,信号就是停车位。
当一个线程进来获取信号,信号量就减一,当一个线程释放信号,信号量就加一。当信号量为0时,就获取不到信号,线程阻塞着,直到有信号量或者超时退出。

//要预先在redis中设置一个key为semaphore,值是一个大于0的数字才可以。
public void testAcquireSemaphore() throws InterruptedException {
		//获取信号量
		RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
		//获取信号,信号量减一,会阻塞当前线程,知道获取到信号为止
		semaphore.acquire();

		System.out.println("获取到了信号");
	}

	public void testReleaseSemaphore(){
		//获取信号量
		RSemaphore semaphore = redissonClient.getSemaphore("semaphore");
		//释放信号,信号量加一
		semaphore.release();

		System.out.println("获取到了信号");
	}
使用信号量来做简单的限流
public void testLimitFlow()  {
		//获取信号量,可以将信号量设置为1000,代表当前系统的当前业务限流为1000,同时只能有1000并发,其他进行服务的降级
		RSemaphore semaphore = redissonClient.getSemaphore("limitFlow");
		//尝试获取信号,信号量减一,会阻塞当前线程,直到获取到信号或者超时5秒返回,超时就返回false,获取到信号就返回true
		boolean succeed = false try {
			succeed = semaphore.tryAcquire(5, TimeUnit.SECONDS);
		} catch (InterruptedException e) {
			
		}
		if (succeed){
			try {
				System.out.println("获取到了信号,执行业务逻辑");
			}finally {
				//释放信号
				semaphore.release();
			}
		}else {
			//没有获取到信号量,直接返回,类似服务降级
		}

	}
可过期性信号量

可过期性信号量的意思是获取的信号可以设置过期时间,如果因为异常或者业务逻辑时间长导致超时后,信号自动释放。

public void testExpireSemaphore() throws InterruptedException {
		RPermitExpirableSemaphore rPermitExpirableSemaphore = redissonClient.getPermitExpirableSemaphore("ExpirableSemaphore");
		
		
		//获取信号,并且设置信号如果3秒内得不到释放就会自动释放,返回值是信号Id。
		String permitId = rPermitExpirableSemaphore.acquire(3, TimeUnit.SECONDS);
		

		try {
			System.out.println("获取到了可过期信号");
			System.out.println("执行业务逻辑");
		}finally {
			//释放信号
			rPermitExpirableSemaphore.release(permitId);
		}
	}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值