基于redis的分布式锁(Java实现)
2018-08-26 17:19:15来源:博客园 阅读 ()
Github源码:
https://github.com/z521598/redis-lock
实现原理:
1.setnx
Redis的setnx指令(文档参考),setnx意为SET if Not eXists,命令格式:setnx $key $value
如果此key不存在,则设置值为value,返回值为1;如果此key存在,则不设置,返回值为0。如下图:
127.0.0.1:6379[1]> setnx key v (integer) 1 127.0.0.1:6379[1]> setnx key v2 (integer) 0
redis是单线程的,是线程安全的,setnx指令由于上述的特性能够满足高并发情况下的对于锁的需求。
2.SpringData-redis
springData-redis是向Redis发送命令以及接受数据的高层次抽象的模版方法,简单理解为“使用java向redis发送命令以及接受数据的客户端”(文档参考)
配置文件:https://github.com/z521598/redis-lock/blob/master/src/main/resources/applicationContext.xml
V1
说明:
最简单的最粗糙的锁实现,实现了2个方法。
方法1:获取锁,public UUID acquire(String lockKey, long acquireTimeoutInMillis, long lockExpiryInMillis)
参数说明:lockKey为锁的key;acquireTimeoutInMillis为获取锁的等待时间,如果超过此时间就放弃锁;lockExpiryInMillis为锁的过期时间。
返回值:锁对应的值,就是命令中“setnx key value”的value。
思路:
1.调用SpringData-Redis的setIfAbsent(lockKey, value)方法(就是命令行中的setnx),value为随机的UUID。
2.如果返回true,则说明已经获取的锁,则继续设置超时时间,返回设置的UUID。
如果返回false,则说明未获取到锁,则休眠100ms,并记录总休眠的时间,如果lockExpiryInMillis大于等于记录总休眠的时间,则说明未获取到锁,返回null。
方法2:释放锁,public void release(String lockKey, UUID uuid)
参数说明:lockKey为锁的key;uuid为锁的value。
思路:检查锁的value是否为uuid,如果相等,则释放,如果不相等,则什么都不做;防止释放了其他线程获取的锁。
明显的缺点:
第一步,获取锁,第二步,然后设置超时时间。这是2步操作,不是原子性操作,如果第一步操作之后,程序崩溃了或者掉电了或者redis恰巧进行了主从切换等等原因,第二步无法正常执行,这样这个锁就永远得不到释放。
代码:
public UUID acquire(String lockKey, long acquireTimeoutInMillis, long lockExpiryInMillis) throws InterruptedException { UUID uuid = UUID.randomUUID(); long timeout = 0L; while (timeout < acquireTimeoutInMillis) { if (redisTemplate.opsForValue().setIfAbsent(lockKey, uuid.toString())) { redisTemplate.expire(lockKey, lockExpiryInMillis, TimeUnit.MILLISECONDS); return uuid; } TimeUnit.MILLISECONDS.sleep(DEFAULT_ACQUIRE_RESOLUTION_MILLIS); timeout += DEFAULT_ACQUIRE_RESOLUTION_MILLIS; } return null; }
锁实现:https://github.com/z521598/redis-lock/blob/master/src/main/java/com/redis/lock/sdata/v1/LockService.java
单元测试:https://github.com/z521598/redis-lock/blob/master/src/test/java/com/redis/lock/sdata/v1/LockServiceTest.java
V2
说明:
大体与V1相同,但是锁的value是"过期的时间",如果获取锁的时候,发现过期时间小于now,则视为锁已经过期。
缺点:(极少出现的情况)
当redis是主从形式的情况下,获取锁之后,master宕机,slave接管,但是这个时候“新master”还未同步锁的key。在这个时候,其他线程去获取锁,发现无此key,则获取了不应该获取的锁,这样就会引起不安全的情况。
代码:
锁实现:https://github.com/z521598/redis-lock/tree/master/src/main/java/com/redis/lock/sdata/v2
单元测试:https://github.com/z521598/redis-lock/blob/master/src/test/java/com/redis/lock/sdata/v2/LockV2ServiceTest.java
V3
敬请期待
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
上一篇:常用的Java Keytool Keystore命令
下一篇:Zuul介绍
- redis缓存 2020-06-12
- Spring Boot 2.3.0 新特性Redis 拓扑动态感应 2020-06-11
- 聊聊微服务架构及分布式事务解决方案! 2020-06-10
- 作为一个面试官,我想问问你Redis分布式锁怎么搞? 2020-06-10
- 分布式锁没那么难,手把手教你实现 Redis 分布锁!|保姆级教 2020-06-08
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash