一、概述
二、使用
2.1 入门
2.1.1 引入依赖
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency>
2.1.2 配置
首先在配置文件中做好redis相关的配置:
spring:redis:host:port: 6379password:
然后创建配置类,按照如下方式配置好Redisson客户端Bean对象:
@Configurationpublic class MyRedissonConfig {@Value("${spring.redis.host}")private String redisHost;@Value("${spring.redis.port}")private String redisPort;@Value("${spring.redis.password}")private String redisPassword;/*** 所有对Redisson的使用都是通过RedissonClient* @return* @throws IOException*/@Bean(destroyMethod="shutdown")public RedissonClient redisson() throws IOException {//1、创建配置Config config = new Config();config.useSingleServer().setAddress("redis://"+ redisHost + ":" + redisPort);config.useSingleServer().setPassword(redisPassword);//2、根据Config创建出RedissonClient实例//Redis url should start with redis:// or rediss://RedissonClient redissonClient = Redisson.create(config);return redissonClient;}}
2.1.2 测试
创建测试类如下,测试客户端是否创建成功:
@Slf4j@RunWith(SpringRunner.class)@SpringBootTestpublic class GulimallProductApplicationTests {@Autowiredprivate RedissonClient redissonClient;@Testpublic void testRedisson() {System.out.println(redissonClient);}}
2.2 分布式锁
2.2.1 可重入锁
基于Redis的Redisson分布式可重入锁RLockJava对象实现了java.util.concurrent.locks.Lock接口。
1. 简单使用
@ResponseBody@GetMapping(value = "/hello")public String hello() {//1、获取一把锁,只要锁的名字一样,就是同一把锁RLock myLock = redisson.getLock("my-lock");//2、加锁myLock.lock(); //阻塞式等待。默认加的锁都是30stry {System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());} catch (Exception ex) {ex.printStackTrace();} finally {//3、解锁 假设解锁代码没有运行,Redisson会不会出现死锁System.out.println("释放锁..." + Thread.currentThread().getId());myLock.unlock();}return "hello";}
<br />在Redisson使用过程中,即使解锁操作没有被正确执行,锁也会在一定时间后被释放掉。锁默认会在30s内自动过期,不会产生死锁问题。默认情况下,如果业务代码没有执行完成,Redisson会通过看门狗机制,运行期间自动锁上新的30s,不用担心业务时间长,锁自动过期被删掉。<br /><br />如果我们在调用加锁方法时指定了锁的过期时间,这时候看门狗机制则会失效,锁在到期之后自动解锁,此时若执行解锁操作,若存在其他线程已重新获取了锁,则会报错,提示解锁失败。<br />
自动续期操作借助于定时任务,每隔指定的时间来扫描业务是否执行完成,如果没有执行完成就执行续期操作,定时任务逻辑如下,可以看到是三分之一的看门狗时间定时执行:
2.2.2 读写锁
基于Redis的Redisson分布式可重入读写锁RReadWriteLock,Java对象实现了java.util.concurrent.locks.ReadWriteLock接口。其中读锁和写锁都继承了RLock接口。
分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。
/*** 保证一定能读到最新数据,修改期间,写锁是一个排它锁(互斥锁、独享锁),读锁是一个共享锁* 写锁没释放读锁必须等待* 只要有读或者写的存都必须等待* @return*/@GetMapping(value = "/write")@ResponseBodypublic String writeValue() {String s = "";RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");RLock rLock = readWriteLock.writeLock();try {//1、改数据加写锁,读数据加读锁rLock.lock();s = UUID.randomUUID().toString();ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();ops.set("writeValue",s);TimeUnit.SECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();} finally {rLock.unlock();}return s;}
@GetMapping(value = "/read")@ResponseBodypublic String readValue() {String s = "";RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock");//加读锁RLock rLock = readWriteLock.readLock();try {rLock.lock();ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();s = ops.get("writeValue");try { TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); }} catch (Exception e) {e.printStackTrace();} finally {rLock.unlock();}return s;}
- 读 + 读 :相当于无锁,并发读,只会在Redis中记录好,所有当前的读锁。他们都会同时加锁成功
- 写 + 读 :必须等待写锁释放
- 写 + 写 :阻塞方式
- 读 + 写 :有读锁。写也需要等待
2.2.3 信号量
基于Redis的Redisson的分布式信号量Semaphore,Java对象RSemaphore采用了与java.util.concurrent.Semaphore相似的接口和用法。
/*** 车库停车* 3车位* 信号量也可以做分布式限流*/@GetMapping(value = "/park")@ResponseBodypublic String park() throws InterruptedException {RSemaphore park = redisson.getSemaphore("park");park.acquire(); //获取一个信号、获取一个值,占一个车位boolean flag = park.tryAcquire();if (flag) {//执行业务} else {return "error";}return "ok=>" + flag;}
@GetMapping(value = "/go")@ResponseBodypublic String go() {RSemaphore park = redisson.getSemaphore("park");park.release(); //释放一个车位return "ok";}
2.2.4 闭锁
基于Redisson的Redisson分布式闭锁CountDownLatch,Java对象RCountDownLatch采用了与java.util.concurrent.CountDownLatch相似的接口和用法。
/*** 放假、锁门* 1班没人了* 5个班,全部走完,我们才可以锁大门* 分布式闭锁*/@GetMapping(value = "/lockDoor")@ResponseBodypublic String lockDoor() throws InterruptedException {RCountDownLatch door = redisson.getCountDownLatch("door");door.trySetCount(5);door.await(); //等待闭锁完成return "放假了...";}
@GetMapping(value = "/gogogo/{id}")@ResponseBodypublic String gogogo(@PathVariable("id") Long id) {RCountDownLatch door = redisson.getCountDownLatch("door");door.countDown(); //计数-1return id + "班的人都走了...";}
