欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

RSemaphore、Redisson 分布式信号器各种使用场景测试

最编程 2024-04-28 08:45:54
...
【开源中国 APP 全新上线】“动弹” 回归、集成大模型对话、畅读技术报告”

1、不做任何设置,直接获取锁

 RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore");
 boolean b = semaphore.tryAcquire();
 if (b){
     try {
         System.out.println("获取到锁");
         ThreadUtil.sleep(3000);
     } finally {
         // 释放锁
         System.out.println("释放锁");
         semaphore.release();
     }
 } else {
     System.out.println("未获取到锁");
 }

当未做任何的设置时候,直接获取该锁是拿不到任何信息的。

打印的数据:

 未获取到锁

 

首次获取锁数量的变化

 RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore2");
 
 int k0 = semaphore.availablePermits();
 System.out.println("许可证可用数量4:" + k0);
 
 // 设置允许的信号量
 semaphore.trySetPermits(3);
 boolean b = semaphore.tryAcquire();
 
 int k1 = semaphore.availablePermits();
 System.out.println("许可证可用数量5:" + k1);
 
 if (b){
     try {
         System.out.println("获取到锁");
         ThreadUtil.sleep(6000);
     } finally {
         // 释放锁
         System.out.println("释放锁");
         semaphore.release();
     }
 } else {
     System.out.println("未获取到锁");
 }
 
 int i = semaphore.availablePermits();
 System.out.println("许可证可用数量6:" + i);

结果

 许可证可用数量4:0
 许可证可用数量5:2
 获取到锁
 许可证可用数量4:2
 许可证可用数量5:1
 获取到锁
 许可证可用数量4:1
 许可证可用数量5:0
 获取到锁
 许可证可用数量4:0
 许可证可用数量5:0
 未获取到锁
 许可证可用数量6:0
 许可证可用数量4:0
 许可证可用数量5:0
 未获取到锁
 许可证可用数量6:0
 释放锁
 许可证可用数量6:1
 释放锁
 许可证可用数量6:2
 释放锁
 许可证可用数量6:3

可见,未设置之前许可证数量的变化是0个,设置后才有许可证。

 

2、在代码中添加信号量,开启2个相同的方法

 @RequestMapping("/v1")
     @ResponseBody
     public String v1(){
         RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore");
         // 设置允许的信号量
         semaphore.trySetPermits(3);
         boolean b = semaphore.tryAcquire();
         if (b){
             try {
                 System.out.println("获取到锁");
                 ThreadUtil.sleep(6000);
             } finally {
                 // 释放锁
                 System.out.println("释放锁");
                 semaphore.release();
             }
         } else {
             System.out.println("未获取到锁");
         }
 
         return "ss";
     }
 
     @RequestMapping("/v2")
     @ResponseBody
     public String v2(){
         RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore");
         // 设置允许的信号量
         semaphore.trySetPermits(3);
         boolean b = semaphore.tryAcquire();
         if (b){
             try {
                 System.out.println("获取到锁");
                 ThreadUtil.sleep(6000);
             } finally {
                 // 释放锁
                 System.out.println("释放锁");
                 semaphore.release();
             }
         } else {
             System.out.println("未获取到锁");
         }
 
         return "ss";
     }

快速点击,分表访问v1和v2,执行结果

 获取到锁
 获取到锁
 获取到锁
 未获取到锁
 未获取到锁
 释放锁
 释放锁
 释放锁

结果说明,当代码在V1发现3个许可证用完后,再访问V1就没法获取到锁了,然后调用V2的方法,V2一样,还是发现锁已经用完,直接返回未获取到锁。

 

3、v1设置1个许可证,v2设置3个许可证

 @RequestMapping("/v1")
     @ResponseBody
     public String v1(){
         RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore3");
 
         int k0 = semaphore.availablePermits();
         System.out.println("许可证可用数量E:" + k0);
 
         // 设置允许的信号量
         semaphore.trySetPermits(1);
         boolean b = semaphore.tryAcquire();
 
         int k1 = semaphore.availablePermits();
         System.out.println("许可证可用数量F:" + k1);
 
         if (b){
             try {
                 System.out.println("获取到锁");
                 ThreadUtil.sleep(6000);
             } finally {
                 // 释放锁
                 System.out.println("释放锁");
                 semaphore.release();
             }
         } else {
             System.out.println("未获取到锁");
         }
 
         int i = semaphore.availablePermits();
         System.out.println("许可证可用数量G:" + i);
 
         return "ss";
     }
 
     @RequestMapping("/v2")
     @ResponseBody
     public String v2(){
         RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore3");
 
         int k0 = semaphore.availablePermits();
         System.out.println("许可证可用数量A:" + k0);
 
         // 设置允许的信号量
         semaphore.trySetPermits(3);
         boolean b = semaphore.tryAcquire();
 
         int k1 = semaphore.availablePermits();
         System.out.println("许可证可用数量B:" + k1);
 
         if (b){
             try {
                 System.out.println("获取到锁");
                 ThreadUtil.sleep(6000);
             } finally {
                 // 释放锁
                 System.out.println("释放锁");
                 semaphore.release();
             }
         } else {
             System.out.println("未获取到锁");
         }
 
         int i = semaphore.availablePermits();
         System.out.println("许可证可用数量C:" + i);
 
         return "ss";
     }

结果

 许可证可用数量E:0
 许可证可用数量F:0
 获取到锁
 许可证可用数量E:0
 许可证可用数量F:0
 未获取到锁
 许可证可用数量G:0
 许可证可用数量A:0
 许可证可用数量B:0
 未获取到锁
 许可证可用数量C:0
 释放锁
 许可证可用数量G:1

任务调用的链路,先执行V1连续请求2次,第二次就获取不到许可证,然后走的v2,v2也获取不到许可证,说明,V1设置的许可证数量,V2设置就无效了。

我又单独再次跑了下V2,发现还是只有一个许可证,说明许可证设置后,应该只能通过 semaphore.addPermits(); 这类方法来增加减少。

第二次跑

 许可证可用数量A:1
 许可证可用数量B:0
 获取到锁
 释放锁
 许可证可用数量C:1

但注意,当我释放3个许可证的时候

发现许可证数量增加了,所以,释放许可证部分一定要注意。

 许可证可用数量A:1
 许可证可用数量B:0
 获取到锁
 释放锁
 许可证可用数量C:3

后来我又试了一个新的,先调用许可证为2个的,再调用许可证为1 的,还是有2个许可证,说明谁先执行设置许可证数量,谁先生效。

 

4、断线后,会自动释放吗?

不会,我模拟了调用V2方法的时候,强制中断,没有走 release 方法,锁没有被释放掉,检查redis可以看到,缓存并没有被清理掉,锁会一直存在。

 

 

5、release 的注意事项

 RSemaphore semaphore = redissonClient.getSemaphore("local_key_semaphore");
         // 设置允许的信号量
         semaphore.trySetPermits(6);
         boolean b = semaphore.tryAcquire();
         if (b){
             try {
                 System.out.println("获取到锁");
                 ThreadUtil.sleep(6000);
             } finally {
                 // 释放锁
                 System.out.println("释放锁");
                 semaphore.release();
             }
         } else {
             System.out.println("未获取到锁");
         }
 
         semaphore.release(8);

如上的代码,本来只有6把锁,但释放的时候释放8,导致现在有8把锁,这一定要注意,release 释放一次,就会增加一把锁。假设你共有5把锁,你用了一把,释放的时候放回去5把,这个时候 5 - 1 + 5 = 9 把锁,这个要特别注意

 

Semaphore 和 PermitExpirableSemaphore 区别

Redisson的可过期性信号量(PermitExpirableSemaphore)实在RSemaphore对象的基础上,为每个信号增加了一个过期时间。每个信号可以通过独立的ID来辨识,释放时只能通过提交这个ID才能释放。

简单的说PermitExpirableSemaphore这个信号量锁,有了过期时间,到时间自动释放。

 

综述

RSemaphore 虽然可以做分布式信号量,但还是有一个致命问题,一旦意外服务中断,重启,锁就不会自动释放,特别是对于任务时间不确定的情况下,又需要控制全局并发量,该问题就尤为突出。