package org.sxkj.common.redis;
|
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.data.redis.core.RedisCallback;
|
import org.springframework.data.redis.core.RedisTemplate;
|
import org.springframework.data.redis.serializer.RedisSerializer;
|
|
import java.util.Objects;
|
import java.util.concurrent.TimeUnit;
|
import java.util.function.Consumer;
|
import java.util.function.Function;
|
import java.util.function.Supplier;
|
|
|
/**
|
* redis 集群共享锁
|
*
|
* @author raojiangang
|
*/
|
public class RedisLockUtils<P, R> {
|
private static final Logger LOG = LoggerFactory.getLogger(RedisLockUtils.class);
|
|
/**
|
* 加锁超时时间,单位毫秒,
|
* 30秒超时
|
*/
|
private static final long LOCK_TIMEOUT = 30 * 1000;
|
|
private RedisTemplate<String, String> primaryStringRedisTemplate;
|
|
public RedisLockUtils(RedisTemplate redisTemplate) {
|
this.primaryStringRedisTemplate = redisTemplate;
|
}
|
|
|
|
/**
|
* 加锁
|
* 取到锁加锁,取不到锁一直等待知道获得锁
|
*
|
* @param lockKey key
|
* @return 时间毫秒
|
*/
|
private synchronized long lock(String lockKey) {
|
while (true) {
|
//锁时间
|
long lockTimeout = currentTimeForRedis() + LOCK_TIMEOUT + 1;
|
if (serializerRedisKey(lockKey, lockTimeout)) {
|
|
//设置超时时间,释放内存
|
primaryStringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
return lockTimeout;
|
} else {
|
//获取redis里面的时间
|
Object result = primaryStringRedisTemplate.opsForValue().get(lockKey);
|
|
Long currLockTimeoutStr = result == null ? null : Long.parseLong(result.toString());
|
//锁已经失效
|
if (currLockTimeoutStr != null && currLockTimeoutStr < System.currentTimeMillis()) {
|
Object andSet = primaryStringRedisTemplate.opsForValue().getAndSet(lockKey, Long.toString(lockTimeout));
|
Long oldLockTimeoutStr = Long.valueOf(
|
Objects.requireNonNull(
|
andSet.toString())
|
);
|
if (oldLockTimeoutStr.equals(currLockTimeoutStr)) {
|
primaryStringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
|
|
return lockTimeout;
|
}
|
}
|
}
|
try {
|
TimeUnit.MILLISECONDS.sleep(200);
|
} catch (InterruptedException e) {
|
e.printStackTrace();
|
}
|
}
|
}
|
|
private Boolean serializerRedisKey(String lockKey, Long lockTimeout) {
|
return primaryStringRedisTemplate.execute((RedisCallback<Boolean>) redisConnection -> {
|
//定义序列化方式
|
RedisSerializer<String> serializer = primaryStringRedisTemplate.getStringSerializer();
|
byte[] value = serializer.serialize(lockTimeout.toString());
|
return (boolean) redisConnection.setNX(lockKey.getBytes(), value);
|
});
|
}
|
|
/**
|
* 解锁
|
*
|
* @param lockKey key
|
* @param lockValue value
|
*/
|
private synchronized void unlock(String lockKey, long lockValue) {
|
LOG.info("==========执行解锁==========");
|
//获取redis中设置的时间
|
Object result = primaryStringRedisTemplate.opsForValue().get(lockKey);
|
Long currentLockTimeoutStr = result == null ? null : Long.valueOf(result.toString());
|
|
//如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁
|
if (currentLockTimeoutStr != null && currentLockTimeoutStr == lockValue) {
|
primaryStringRedisTemplate.delete(lockKey);
|
LOG.info("==========解锁成功==========");
|
}
|
}
|
|
/**
|
* 获取服务时间
|
*
|
* @return 时间戳
|
*/
|
private long currentTimeForRedis() {
|
return primaryStringRedisTemplate.execute(
|
(RedisCallback<Long>) redisConnection -> redisConnection.time());
|
}
|
|
|
/**
|
* redis 全局锁
|
* 有参有返回值
|
*
|
* @param lockKey key
|
* @param function fun
|
*/
|
public R tryLock(String lockKey, P param, Function<P, R> function) {
|
R result = null;
|
try {
|
long lockTime = lock(lockKey);
|
result = function.apply(param);
|
//释放锁
|
unlock(lockKey, lockTime);
|
} catch (Exception e) {
|
throw e;
|
} finally {
|
deleteKey(lockKey);
|
}
|
return result;
|
}
|
|
/**
|
* redis 全局锁
|
* 有参有返回值
|
*
|
* @param lockKey key
|
* @param function fun
|
*/
|
public R tryLock(String lockKey, Supplier<R> function) {
|
R result = null;
|
try {
|
long lockTime = lock(lockKey);
|
result = function.get();
|
//释放锁
|
unlock(lockKey, lockTime);
|
} catch (Exception e) {
|
throw e;
|
} finally {
|
deleteKey(lockKey);
|
}
|
return result;
|
}
|
|
/**
|
* redis 全局锁
|
* 有参 无返回值
|
*
|
* @param lockKey key
|
* @param consumer fun
|
*/
|
public void tryLock(String lockKey, P param, Consumer<P> consumer) {
|
try {
|
long lockTime = lock(lockKey);
|
//执行
|
consumer.accept(param);
|
//释放锁
|
unlock(lockKey, lockTime);
|
} finally {
|
deleteKey(lockKey);
|
}
|
}
|
|
/**
|
* redis 全局锁
|
* 无参无返回值
|
*
|
* @param lockKey key
|
* @param runnable fun
|
*/
|
public void tryLock(String lockKey, Runnable runnable) {
|
try {
|
long lockTime = lock(lockKey);
|
//执行
|
runnable.run();
|
//释放锁
|
unlock(lockKey, lockTime);
|
|
} finally {
|
deleteKey(lockKey);
|
}
|
}
|
|
private void deleteKey(String lockKey) {
|
primaryStringRedisTemplate.delete(lockKey);
|
}
|
|
}
|