用SETNX、GET、GETSET做分布式锁
GETSET命令
该命令接收两个参数:key,value。命令效果为,设置key的值为value,同时返回该key存储的旧值。该操作是原子操作。
使用步骤
-
计算expireTime = 当前时间 + 过期超时时间,执行SETNX key expireTime,如果返回1,则代表获取锁成功;如果返回0,
则没有获取到锁,执行第2步。
-
GET key 获取oldExpireTime,并与当前时间进行比较,如果小于当前时间,则认为这个锁已经超时,可以允许别的请求重新获取,执行第3步。
-
计算newExpireTime = 当前时间 + 过期超时时间,执行GETSET key,newExpireTime 会返回currentExpireTime。
-
判断currentExpireTime 与 oldExpireTime是否相等,如果相等,说明获取锁成功,如果不相等,说明获取锁失败。
-
获取锁之后,当前线程执行业务处理,处理完毕后,应检查锁对应的过期时间是否大于当前时间,如果是,则执行DEL命令释放锁。
伪代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public final class RedisLockUtil { public static boolean lock(String key, int expire) { RedisService redisService = SpringUtils.getBean(RedisService.class);
long expireTime = System.currentTimeMillis() + expire; long status = redisService.setnx(key, String.valueOf(expireTime));
if(status == 1) { return true; } long oldExpireTime = Long.parseLong(redisService.get(key, "0")); if(oldExpireTime < System.currentTimeMillis()) { long newExpireTime = System.currentTimeMillis() + expire; long currentExpireTime = Long.parseLong(redisService.getSet(key, String.valueOf(newExpireTime))); if(currentExpireTime == oldExpireTime) { return true; } } return false; }
public static void unLock(String key) { RedisService redisService = SpringUtils.getBean(RedisService.class); long oldExpireTime = Long.parseLong(redisService.get(key, "0")); if(oldExpireTime > System.currentTimeMillis()) { redisService.del(key); } } }
|
原文链接