import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Redis分布式锁
*/
@Component
@Slf4j
public class RedisLock {
@Resource
private StringRedisTemplate stringRedisTemplate;
private static final long EXPIRE = 30 * 1000L; // 失效时间
private static final long TIMEOUT = 10 * 1000L; // 等待时间
/**
* 分布式加锁
* @param key
* @param value requestTime + expireTime
* @return
*/
public boolean secKilllock(String key,String value){
if(stringRedisTemplate.opsForValue().setIfAbsent(key,value)){//对应setnx命令
log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{}",key,value);
return true;
}
//判断锁超时
String currentValue = stringRedisTemplate.opsForValue().get(key);
log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{} currentValue:{}",key,value, currentValue);
//如果锁过期
if(!StringUtils.isEmpty(currentValue) && Long.parseLong(currentValue) < System.currentTimeMillis()){//currentValue不为空且小于当前时间
//获取上一个锁的时间value
String oldValue = stringRedisTemplate.opsForValue().getAndSet(key,value);//对应getset,如果key存在
log.info("[Redis分布式锁 secKilllock()]获取锁成功 kye:{},value:{} currentValue:{} oldValue:{}",key,value, currentValue, oldValue);
if(!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue) ){
//oldValue不为空且oldValue等于currentValue,也就是校验是不是上个时间戳,也是防止并发
return true;
}
}
log.info("[Redis分布式锁 secKilllock()]获取锁失败 kye:{},value:{}",key,value);
return false;
}
/**
* 解锁
* @param key
* @param value
*/
public void unlock(String key,String value){
try {
String currentValue = stringRedisTemplate.opsForValue().get(key);
log.info("[Redis分布式锁 unlock] 解锁 key:{} value:{} currentValue:{}", key, value, currentValue);
if(!StringUtils.isEmpty(currentValue) && currentValue.equals(value) ){
log.info("[Redis分布式锁 unlock] 解锁 key:{} value:{} currentValue:{} 删除KEY开始", key, value, currentValue);
stringRedisTemplate.opsForValue().getOperations().delete(key);//删除key
}
} catch (Exception e) {
log.error("[Redis分布式锁 unlock] 解锁出现异常了,{}", e);
}
}
/**
* 加锁 获取锁失败会重试获取锁
* @param key
* @param value
* @return
*/
public boolean lock(String key,String value){
log.info("获取锁 key:{},value:{}",key,value);
//请求锁时间
long requestTime = System.currentTimeMillis();
while (true) {
//等待锁时间
long watiTime = System.currentTimeMillis() - requestTime;
//如果等待锁时间超过10s,加锁失败
if(watiTime > TIMEOUT){
log.info("等待锁超时 kye:{},value:{}",key,value);
return false;
}
if(stringRedisTemplate.opsForValue().setIfAbsent(key,String.valueOf(System.currentTimeMillis()))){
//获取锁成功
log.info("[Redis分布式锁]获取锁成功 kye:{},value:{}",key,value);
//设置超时时间,防止解锁失败,导致死锁
stringRedisTemplate.expire(key, EXPIRE, TimeUnit.MILLISECONDS);
return true;
}
String valueTime = stringRedisTemplate.opsForValue().get(key);
if(! StringUtils.isEmpty(valueTime) && System.currentTimeMillis() - Long.parseLong(valueTime) > EXPIRE){
//加锁时间超过过期时间,删除key,防止死锁
log.info("[Redis分布式锁]锁超时, key:{}, value:{}", key, value);
try{
stringRedisTemplate.opsForValue().getOperations().delete(key);
}catch (Exception e){
log.info("[Redis分布式锁]删除锁异常 key:{}, value:{}", key, value);
e.printStackTrace();
}
return false;
}
//获取锁失败,等待20毫秒继续请求
try {
log.info("[Redis分布式锁]获取锁失败等待20 nanoSeconds key:{},value:{}",key,value);
TimeUnit.NANOSECONDS.sleep(20);
} catch (InterruptedException e) {
log.info("等待20 nanoSeconds 异常 key:{},value:{}",key,value);
e.printStackTrace();
}
}
}
}
来源:oschina
链接:https://my.oschina.net/u/563338/blog/3221456