import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
/**
* 使用redis实现分布式锁
*/
public class RedisLock {
private static Logger log = LoggerFactory.getLogger(RedisLock.class);
private StringRedisTemplate stringRedisTemplate;
private String lockKey;//竞争资源的标志
private String value;
private static final String PREFIX_KEY = "gb.common.distributed.lock.redis_";
private boolean locked=false;
/**
* 锁超时时间,防止线程在入锁以后,无限的执行等待
*/
private int expireSecond = 60;//秒
private int timeout=0;//秒
private static final int DEFAULT_RETRY_INTERVAL_MILLIS = 100;//100ms
//重试次数
private int retryCount = 0;
//重试间隔
private int retryIntervalMillisecond= 0;//ms
@Deprecated
public RedisLock(StringRedisTemplate stringRedisTemplate,String lockKey) {
this.stringRedisTemplate = stringRedisTemplate;
this.lockKey = PREFIX_KEY + lockKey;
}
public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond) {
this.stringRedisTemplate = stringRedisTemplate;
this.lockKey = PREFIX_KEY + lockKey;
this.expireSecond = expireSecond;
}
public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond,int timeout) {
this(stringRedisTemplate, lockKey, expireSecond);
this.timeout= timeout;
}
public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond,int retryCount,int retryIntervalMillisecond) {
this(stringRedisTemplate, lockKey, expireSecond);
this.retryCount= retryCount;
this.retryIntervalMillisecond=retryIntervalMillisecond;
}
// 获取锁
@Deprecated
public boolean acquireLock(long expireSecond) {
if (expireSecond<=0){
throw new RuntimeException("获取redids分布锁,过期秒数应大于零!");
}
this.expireSecond=(int)expireSecond;
return acquireLock();
}
// 获取锁
@Deprecated
public boolean acquireLock() {
return lock();
}
// 获取锁
public boolean lock() {
log.debug("begin redisLock lock");
//锁时间
long lockTimeout = getCurrentTimeMillisFromRedis()+ expireSecond*1000 +1;
String strLockTimeout=String.valueOf(lockTimeout);
// 通过SETNX试图获取一个lock
if (setNX(lockKey, strLockTimeout, expireSecond)) {// SETNX成功,则成功获取一个锁
this.locked=true;
this.value=strLockTimeout;
log.debug("setNX成功");
log.debug("end redisLock lock");
return true;
}
//获取redis里面的时间
String strCurrentLockTimeout= stringRedisTemplate.opsForValue().get(lockKey);
log.debug("lockKey:{},strCurrentLockTimeout:{}",lockKey,strCurrentLockTimeout);
//锁已经失效
if (strCurrentLockTimeout != null && Long.parseLong(strCurrentLockTimeout)
log.debug("锁已过期!");
//判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行
//获取上一个锁到期时间,并设置现在的锁到期时间
String strOldLockTimeout=stringRedisTemplate.opsForValue().getAndSet(lockKey, strLockTimeout);
if (strOldLockTimeout != null && strOldLockTimeout.equals(strCurrentLockTimeout)){
log.debug("重新抢到锁");
//多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁
//设置超时间,释放内存
stringRedisTemplate.expire(lockKey, expireSecond*1000, TimeUnit.MILLISECONDS);
this.value=strLockTimeout;
this.locked=true;
log.debug("end redisLock lock");
return true;
}
}
log.debug("未抢到锁");
log.debug("end redisLock lock");
return false;
}
// 获取锁
public boolean tryLock() {
if (retryCount>0 && retryIntervalMillisecond>0){
do {
if (lock()){
return true;
}
try {
Thread.sleep(retryIntervalMillisecond);
} catch (InterruptedException e) {
e.printStackTrace();
}
retryCount--;
} while (retryCount>0);
} else {
do {
if (lock()){
return true;
}
timeout -= DEFAULT_RETRY_INTERVAL_MILLIS;
} while (timeout>0);
}
return false;
}
//释放锁
@Deprecated
public void releaseLock() {//同义词
unlock();
}
//释放锁
public void unlock() {
log.debug("begin redisLock unlock");
if (this.locked){
//获取redis里面的时间
String strCurrentLockTimeout= stringRedisTemplate.opsForValue().get(lockKey);
if (strCurrentLockTimeout==null){//redis已清了
log.debug("锁已不存在了");
} else {
/* if (this.value.equals(strCurrentLockTimeout)){
log.debug("锁内容是自己放的,自己删除!");
stringRedisTemplate.delete(lockKey);
} */
stringRedisTemplate.delete(lockKey);
/*
//还未过期,且是自己加锁,删除之
Long currentTimeMillisFromRedis=getCurrentTimeMillisFromRedis();
if (currentTimeMillisFromRedis>Long.parseLong(strCurrentLockTimeout)){//过期了
log.debug("锁已过期,啥也不做!currentTimeMillisFromRedis:{},strCurrentLockTimeout:{}",currentTimeMillisFromRedis,strCurrentLockTimeout);
} else {
log.debug("锁未过期!");
if (this.value.equals(strCurrentLockTimeout)){
log.debug("锁内容是自己放的,自己删除!");
stringRedisTemplate.delete(lockKey);
} else {
log.debug("锁内容是不是自己放的,应该不执行不到!");
}
} */
}
this.locked=false;
} else {
log.debug("原来就没锁住");
}
log.debug("end redisLock unlock");
}
private boolean setNX(final String key, final String value, final long second) {
return (Boolean) stringRedisTemplate.execute(new RedisCallback() {
public Boolean doInRedis(RedisConnection connection) {
byte[] keyBytes = stringRedisTemplate.getStringSerializer().serialize(key);
boolean locked = connection.setNX(keyBytes, stringRedisTemplate.getStringSerializer().serialize(value));
if(locked && second>0L){
connection.expire(keyBytes, second);
}
return locked;
}
});
}
//获取redis时间,避免多服务的时间不一致问题
public long getCurrentTimeMillisFromRedis(){
return stringRedisTemplate.execute(new RedisCallback() {
@Override
public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
return redisConnection.time();
}
});
}
/**
* 获取锁的名称
* @return
*/
public String getLockKey() {
return this.lockKey;
}
/**
* 获取是否已经获取锁
* @return
*/
public boolean islocked() {
return this.locked;
}
}