Redis实现分布式锁(用于用户下单)
Redis Setnx(SET if Not eXists) 命令在指定的 key 不存在时,为 key 设置指定的值。
实例
redis> EXISTS job # job 不存在
(integer) 0
redis> SETNX job "programmer" # job 设置成功
(integer) 1
redis> SETNX job "code-farmer" # 尝试覆盖 job ,失败
(integer) 0
redis> GET job # 没有被覆盖
"programmer"
jedis.setnx
- 1,当 key 的值被设置 (如果 SETNX 返回1,说明该进程获得锁,SETNX将键 lock.foo 的值设置为锁的超时时间(当前时间 + 锁的有效时间)。)
- 0,当 key 的值没被设置(如果 SETNX 返回0,说明其他进程已经获得了锁,进程不能进入临界区。进程可以在一个循环中不断地尝试 SETNX 操作,以获得锁。)
public synchronized boolean acquireLock(Long planId) {
try {
//用于控制循环次数 循环一次 -100
int timeout = 600;
int expireMsecs = 3000; // 锁失效时长
while (timeout >= 0) {
long expires = System.currentTimeMillis() + expireMsecs + 1;
String expiresStr = String.valueOf(expires); //锁到期时间
// 拼接锁的key 固定前缀+planId
if (zhaodeJedisCache.setnx(CONSULT_ORDER_REDIS_LOCK+planId, expiresStr) == 1) {
log.info("!~~~~~~~~~~ 当前登陆的Id为:"+super.getLoginUserId()+" 获取到锁了:锁"+CONSULT_ORDER_REDIS_LOCK+planId);
return true;
}
//没有得到锁,先取出来锁的过期时间
String currentValueStr = zhaodeJedisCache.get(CONSULT_ORDER_REDIS_LOCK+planId); //redis里的时间
log.info("!~~~~~~~~~~ 当前登陆的Id为:"+super.getLoginUserId()+" 原来有锁 锁为"+CONSULT_ORDER_REDIS_LOCK+planId+" currentValueStr:"+currentValueStr);
if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
//判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
// lock is expired
//原有的锁已经过期
log.info("!~~~~~~~~~~ 当前登陆的Id为:"+super.getLoginUserId()+" 原来有锁 锁 "+CONSULT_ORDER_REDIS_LOCK+planId+" 已过期 currentValueStr:"+currentValueStr);
String oldValueStr = zhaodeJedisCache.getSet(CONSULT_ORDER_REDIS_LOCK+planId, expiresStr);
//获取上一个锁到期时间,并设置现在的锁到期时间,
//只有一个线程才能获取上一个线上的设置时间,因为zhaodeJedisCache.getSet是同步的
if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
//如过这个时候,多个线程恰好都到了这里,但是只有一个线程的设置值和当前值相同,他才有权利获取锁
// lock acquired
// 这个时候,如果getSet取得值与刚才获取锁的时候取得值一样,所以获取到了锁,
//locked = true;
log.info("!~~~~~~~~~~ 当前登陆的Id为:"+super.getLoginUserId()+" 原来有锁 锁 "+CONSULT_ORDER_REDIS_LOCK+planId+" 已过期 从新设置的新的锁:");
return true;
}
}
timeout -= 100;
Thread.sleep(500);
}
} catch (InterruptedException e) {
log.error("!~~~~~~~~~~~~~~~~~ 倾诉下单,获取锁失败,error:"+e.getMessage());
// zhaodeJedisCache.del(CONSULT_ORDER_REDIS_LOCK+planId);
e.printStackTrace();
}
return false;
}