您的位置:首页 > 数据库 > Redis

Redis SETNX命令实现分布式锁

2017-04-25 18:27 417 查看

SETNX命令简介

命令格式

SETNX key value

将 key 的值设为 value,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是SET if Not eXists的简写。

返回值

设置成功,返回 1 。

设置失败,返回 0 。



使用SETNX实现分布式锁

/**
* 使用Redis实现分布式全局锁
* 算法实现参考:http://redis.io/commands/setnx
* @author shenhaiwen
*
*/
public class RedisDistributeLock {
private String lockName;
private long expireTime;
private RedisOperator redisOper;

/**
*
* @param lockName 同一个名称表示同一把锁
* @param expireTime 锁的超时时间,如果超过,该锁失效,其他线程可获取锁
* @param redisOper
*/
public RedisDistributeLock(String lockName,long expireTime,RedisOperator redisOper){
this.lockName = lockName;
this.redisOper = redisOper;
this.expireTime = expireTime * 1000;
}

private String getLockKey(){
if(StringUtils.isBlank(lockName))
return null;

return "lock:" + lockName;
}

/**
* 抛出异常或返回-1,说明未获取到锁
* return: 返回值需要在unlock时作为参数传入
* @param timeout -1:永久等待
* @throws Exception
*/
public long lock(long timeout) throws Exception{
String key = getLockKey();

if(key == null)
throw new Exception("lock must have a name");

boolean infinite = (timeout == -1);
while(infinite || timeout >= 0){
Long value = System.currentTimeMillis() + expireTime;
long keyTimeout = value.longValue();
long setnxRet = redisOper.insertKeyValueIfNotExist(key, value.toString(), -1, null);

if(setnxRet > 0){
return keyTimeout; //暂无其他线程或进程加锁
}
if(setnxRet == -1){
return -1;
}
//已经有锁了
String val = redisOper.getValueByKey(key);
if(StringUtils.isBlank(val)){
//key刚好被删除了,重来
continue;
}
if(System.currentTimeMillis() > Long.parseLong(val)){
Long newVal = System.currentTimeMillis() + expireTime;
String oldVal = redisOper.getset(key, newVal.toString(), -1, null);
if(StringUtils.isBlank(val)){
//key刚好被删除了,重来
continue;
}
if(System.currentTimeMillis() > Long.parseLong(oldVal)){
keyTimeout = newVal.longValue();
return keyTimeout;
}
}

}

if(!infinite){
timeout -= 100;
}

Thread.sleep(100);

return -1;
}

/**
*
* @param keyTimeout  lock的返回值
*/
public void unlock(long keyTimeout){
if(System.currentTimeMillis() > keyTimeout){
return;
}
redisOper.deleteKeyValue(getLockKey());
}

public String getLockName() {
return lockName;
}

public long getExpireTime() {
return expireTime;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: