package com.itheima.utils;
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;
import java.util.concurrent.TimeUnit;
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;
private int expireSecond;
private int timeout;
private static final int DEFAULT_RETRY_INTERVAL_MILLIS = 100;
private int retryCount;
private int retryIntervalMillisecond;
/**
* @deprecated
*/
@Deprecated
public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey) {
this.locked = false;
this.expireSecond = 60;
this.timeout = 0;
this.retryCount = 0;
this.retryIntervalMillisecond = 0;
this.stringRedisTemplate = stringRedisTemplate;
this.lockKey = "gb.common.distributed.lock.redis_" + lockKey;
}
public RedisLock(StringRedisTemplate stringRedisTemplate, String lockKey, int expireSecond) {
this.locked = false;
this.expireSecond = 60;
this.timeout = 0;
this.retryCount = 0;
this.retryIntervalMillisecond = 0;
this.stringRedisTemplate = stringRedisTemplate;
this.lockKey = "gb.common.distributed.lock.redis_" + 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
*/
@Deprecated
public boolean acquireLock(long expireSecond) {
if (expireSecond <= 0L) {
throw new RuntimeException("获取redids分布锁,过期秒数应大于零!");
} else {
this.expireSecond = (int) expireSecond;
return this.acquireLock();
}
}
/**
* @deprecated
*/
@Deprecated
public boolean acquireLock() {
return this.lock();
}
public boolean lock() {
log.debug("begin redisLock lock");
// value = 时间戳(redis客户端) + 过期时间(毫秒) + 1L
long lockTimeout = this.getCurrentTimeMillisFromRedis() + (long) (this.expireSecond * 1000) + 1L;
// 将value转成String类型
String strLockTimeout = String.valueOf(lockTimeout);
/**
* 上锁, 调用redis原生的setNX方法
* -如果返回true, 表示Redis中没有这个key, 上锁成功
* -如果返回false, 表示Redis中已经存在这个key, 抢锁失败
*/
if (this.setNX(this.lockKey, strLockTimeout, (long) this.expireSecond)) {
// 设置上锁状态为true
this.locked = true;
this.value = strLockTimeout;
log.debug("setNX成功");
log.debug("end redisLock lock");
return true;
} else {
// 抢锁失败, 先拿到redis中当前key对应的value, 这个value代表当前锁的失效时间
String strCurrentLockTimeout = (String) this.stringRedisTemplate.opsForValue().get(this.lockKey);
log.debug("lockKey:{},strCurrentLockTimeout:{}", this.lockKey, strCurrentLockTimeout);
// 判断锁是否失效了
if (strCurrentLockTimeout != null && Long.parseLong(strCurrentLockTimeout) < this.getCurrentTimeMillisFromRedis()) {
log.debug("锁已过期!");
// getAndSet:获取原来key键对应的值并重新赋新值。
String strOldLockTimeout = (String) this.stringRedisTemplate.opsForValue().getAndSet(this.lockKey, strLockTimeout);
//
if (strOldLockTimeout != null && strOldLockTimeout.equals(strCurrentLockTimeout)) {
log.debug("重新抢到锁");
this.stringRedisTemplate.expire(this.lockKey, (long) (this.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 (this.retryCount > 0 && this.retryIntervalMillisecond > 0) {
do {
if (this.lock()) {
return true;
}
try {
Thread.sleep((long) this.retryIntervalMillisecond);
} catch (InterruptedException var2) {
var2.printStackTrace();
}
--this.retryCount;
} while (this.retryCount > 0);
} else {
do {
if (this.lock()) {
return true;
}
this.timeout -= 100;
} while (this.timeout > 0);
}
return false;
}
/**
* @deprecated
*/
@Deprecated
public void releaseLock() {
this.unlock();
}
public void unlock() {
log.debug("begin redisLock unlock");
if (this.locked) {
String strCurrentLockTimeout = (String) this.stringRedisTemplate.opsForValue().get(this.lockKey);
if (strCurrentLockTimeout == null) {
log.debug("锁已不存在了");
} else {
this.stringRedisTemplate.delete(this.lockKey);
}
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) this.stringRedisTemplate.execute(new RedisCallback<Boolean>() {
public Boolean doInRedis(RedisConnection connection) {
byte[] keyBytes = RedisLock.this.stringRedisTemplate.getStringSerializer().serialize(key);
boolean locked = connection.setNX(keyBytes, RedisLock.this.stringRedisTemplate.getStringSerializer().serialize(value));
if (locked && second > 0L) {
connection.expire(keyBytes, second);
}
return locked;
}
});
}
/**
* 获取redis客户端的系统时间(毫秒)
*/
public long getCurrentTimeMillisFromRedis() {
return (Long) this.stringRedisTemplate.execute(new RedisCallback<Long>() {
public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
return redisConnection.time();
}
});
}
public String getLockKey() {
return this.lockKey;
}
public boolean islocked() {
return this.locked;
}
}