package com.campuscard.core.utils;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
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 org.springframework.data.redis.serializer.RedisSerializer;
public class RedisLock {
// 加锁超时时间,单位毫秒, 即:加锁时间内执行完操作,如果未完成会有并发现象
private static final long LOCK_TIMEOUT = 3 * 1000;
private StringRedisTemplate stringRedisTemplate;
public RedisLock(StringRedisTemplate stringRedisTemplate) {
this.stringRedisTemplate = stringRedisTemplate;
}
/**
* 加锁 取到锁加锁,取不到锁一直等待知道获得锁
*
* @param lockKey
* @param threadName
* @return
*/
public synchronized long lock(String lockKey) {
while (true) { // 循环获取锁
// 锁时间
Long lock_timeout = currtTimeForRedis() + LOCK_TIMEOUT + 1;
if (stringRedisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection redisConnection) throws DataAccessException {
// 定义序列化方式
RedisSerializer<String> serializer = stringRedisTemplate.getStringSerializer();
byte[] value = serializer.serialize(lock_timeout.toString());
boolean flag = redisConnection.setNX(lockKey.getBytes(), value);
return flag;
}
})) {
// 如果加锁成功
// 设置超时时间,释放内存
stringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
return lock_timeout;
} else {
// 获取redis里面的时间
String result = stringRedisTemplate.opsForValue().get(lockKey);
Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result);
long time = new Date().getTime();
// 锁已经失效
if (currt_lock_timeout_str != null && currt_lock_timeout_str < time) {
// 判断是否为空,不为空时,说明已经失效,如果被其他线程设置了值,则第二个条件判断无法执行
// 获取上一个锁到期时间,并设置现在的锁到期时间
String str = stringRedisTemplate.opsForValue().getAndSet(lockKey, lock_timeout.toString());
if(StringUtils.isNotBlank(str)) {
Long old_lock_timeout_Str = Long.parseLong(str);
if (old_lock_timeout_Str != null && old_lock_timeout_Str.equals(currt_lock_timeout_str)) {
// 多线程运行时,多个线程签好都到了这里,但只有一个线程的设置值和当前值相同,它才有权利获取锁
// 设置超时间,释放内存
stringRedisTemplate.expire(lockKey, LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
// 返回加锁时间
return lock_timeout;
}
}
}
}
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 解锁
*
* @param lockKey
* @param lockValue
* @param threadName
*/
public synchronized void unlock(String lockKey, long lockValue) {
// 获取redis中设置的时间
String result = stringRedisTemplate.opsForValue().get(lockKey);
Long currt_lock_timeout_str = result == null ? null : Long.parseLong(result);
// 如果是加锁者,则删除锁, 如果不是,则等待自动过期,重新竞争加锁
if (currt_lock_timeout_str != null && currt_lock_timeout_str == lockValue) {
stringRedisTemplate.delete(lockKey);
}
}
/**
* 多服务器集群,使用下面的方法,代替System.currentTimeMillis(),获取redis时间,避免多服务的时间不一致问题!!!
*
* @return
*/
public long currtTimeForRedis() {
return stringRedisTemplate.execute(new RedisCallback<Long>() {
@Override
public Long doInRedis(RedisConnection redisConnection) throws DataAccessException {
return redisConnection.time();
}
});
}
}
来源:https://www.cnblogs.com/feiyun126/p/8573215.html