RSemaphore、Redisson 分布式信号器各种使用场景测试
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 虽然可以做分布式信号量,但还是有一个致命问题,一旦意外服务中断,重启,锁就不会自动释放,特别是对于任务时间不确定的情况下,又需要控制全局并发量,该问题就尤为突出。