当前位置 : 主页 > 编程语言 > java >

redis缓存-分布式锁原理与使用

来源:互联网 收集:自由互联 发布时间:2023-03-22
本地锁 只能锁定自己的服务 SET key value [EX seconds] [PX milliseconds] [NX|XX]http://www.redis.cn/commands/set.html public MapString, ListCatelog2Vo getCatalogJsonWithRedisLock() { //redis 分布原则 加锁保证原子性,解
  • 本地锁 只能锁定自己的服务 image.png

  • SET key value [EX seconds] [PX milliseconds] [NX|XX] http://www.redis.cn/commands/set.html

public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedisLock() { //redis 分布原则 加锁保证原子性,解锁保证原子性 //1.占用分布式锁 去redis站坑setIfAbsent = SET NX,随便set K V数值,之后返回一个结果 =>2.设置过期时间和枷锁是同步的原子的 String uuid = UUID.randomUUID().toString(); //设置v值是uuid Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS); if (lock) { System.out.println("获取分布式锁成功..."); //1.1加锁成功 执行业务 //2.设置期时间 以免造成死锁 设置 30秒自动删除 ;设置过期时间和枷锁是同步的原子的 Map<String, List<Catelog2Vo>> dataFromDb; try { //redisTemplate.expire("lock",30,TimeUnit.SECONDS); dataFromDb = getDataFromDb(); } finally { String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end"; Long lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid); } //3.删除 不是自己锁 获取数值对比+对比删除成功=原子操作 lua 解锁操作 /** * String lockValue = redisTemplate.opsForValue().get("lock"); //查到锁的数值 * //2.1查看放的uuid和lockValue一致 说明 锁是自己加的 在执行删除自己 锁 * if (uuid.equals(lockValue)){ * //1.2执行成功之后 解锁/删除锁 共其他使用 * redisTemplate.delete("lock"); * } */ return dataFromDb; } else { //枷锁失败。。在重置,把自己在调一遍 try { Thread.sleep(200); }catch (Exception e){ } System.out.println("获取分布式锁失败 重试..."); return getCatalogJsonWithRedisLock();//自旋方式 } }

导入pom

<!--用redisson作为分布式锁,分布式对象 --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.12.0</version> </dependency>

配置MyRedissonConfig

@Configuration public class MyRedissonConfig { /** * 对于redisson配置是RedissonClient操作的 * * @return * @throws IOException */ @Bean(destroyMethod = "shutdown") RedissonClient redisson() throws IOException { //创建配置 Config config = new Config(); config.useSingleServer().setAddress("redis://192.168.56.10:6379"); //config.useClusterServers() 集群模式 // .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001"); //依据Config创建RedissonClient RedissonClient redissonClient = Redisson.create(config); return redissonClient; } }

测试加锁 解锁

public String hello() { //1.获取一把锁 只要锁 名字一样就是用一把锁 RLock lock = redisson.getLock("my_lock"); //2.加锁 lock.lock(30, TimeUnit.SECONDS); //lock.lock(); //堵塞式等待 try { //打印线程好 如果服务中断 有个看门狗自动续期 System.out.println("加锁成功执行业务"+Thread.currentThread().getId()); Thread.sleep(30000); } catch (InterruptedException e) { } finally { //3.解锁 System.out.println("解锁成功执行业务"+Thread.currentThread().getId()); lock.unlock(); } return "hello"; }

//2.加锁 锁的自动续期,如果业务超长,运行期间自动给锁续上行的时间,不用担心业务时间长 锁自动过期被删掉 // 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除。

  • 10秒自动解锁,自动解锁时间-定要大于业务的执行时间。在锁时间到了以后,不会自动续期。
  • 如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
  • 如果我们未指定锁的超时时间,就使用30 * 1000 [LockWatchdogTimeout看门狗的默认时间] ;
  • 只要占锁成功,就会启动一个定时任务[重新给锁设置过期时间,新的过期时间就是看[ ]狗的默认时间]
  • internallockLeaseTime [看门狗时间] / 3, 10s 规则用 lock.lock(30, TimeUnit.SECONDS); 加锁时间>=解锁时间

读写锁测试

  • 保证一 定能读到最新数据,修改期间,写锁是一 个排他锁(互斥锁,独享锁)。读锁是一 一个共享锁
  • 与锁役释放读就必须寺待
  • 读+读 相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
  • 写+读:等待写锁释放
  • 写+写:阻塞方式
  • 读+写:有读锁。写也需要等待。
  • 只要有写的存在,都必须等待
@ResponseBody @GetMapping("/write") //读写锁 public String writeValue() { String s = ""; RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");//加写锁 RLock rLock = lock.writeLock(); try { //改数据加写锁,读数据加读锁 rLock.lock(); s = UUID.randomUUID().toString(); redisTemplate.opsForValue().set("writeValue", s); Thread.sleep(30000); } catch (InterruptedException e) { e.printStackTrace(); } finally { rLock.unlock(); } return s; } @ResponseBody @GetMapping("/read") //读写锁 public String readValue() { String s = ""; RReadWriteLock lock = redisson.getReadWriteLock("rw-lock"); RLock rLock = lock.readLock(); //加读锁 //执行业务代码 rLock.lock(); try { s = redisTemplate.opsForValue().get("writeValue"); } catch (Exception e) { e.printStackTrace(); } finally { rLock.unlock(); } return s; }

Redisson-信号量测试

车库停车 信号量测试 限流使用

@GetMapping("park") @ResponseBody public String park() throws InterruptedException { //使用信号量命名 RSemaphore park = redisson.getSemaphore("park"); //获取一个信号 获取一个数值 占一个车位 获取失败会一直等待 boolean b = park.tryAcquire(); //尝试获取 能否取到数值 不行就false 走 if (b){ //执行代码 }else { return "error"; } park.acquire(); return "ok停车+1"; } @GetMapping("go") @ResponseBody public String go() throws InterruptedException { //使用信号量命名 RSemaphore park = redisson.getSemaphore("park"); //释放信号 park.release(); return "ok 开走-1"; }

Redisson-闭锁测试

分布式闭锁 放假锁门 等到全部走完 才关锁

@GetMapping("/lockDoor") @ResponseBody public String lockDoor() throws InterruptedException { RCountDownLatch doot = redisson.getCountDownLatch("door"); doot.trySetCount(5);//等待5个 doot.await();//等待闭锁都完成 return "放假了"; } @GetMapping("/gogo/{id}")//接收走的id @ResponseBody public String gogo(@PathVariable("id") Long id){ RCountDownLatch doot = redisson.getCountDownLatch("door"); doot.countDown();//等价于计数减一 return id+"走了"; }

修正之前方法

public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedissonLock() { //去占锁 RLock lock = redisson.getLock("catalogJson-lock"); //占锁之后 加锁 lock.lock(); Map<String, List<Catelog2Vo>> dataFromDb; try { dataFromDb = getDataFromDb(); } finally { //解锁 lock.unlock(); } return dataFromDb; }

缓存-分布式锁-缓存一致性解决

image.png image.png image.png image.png

上一篇:在 Java 中使用集合框架
下一篇:没有了
网友评论