原理:redis接受网络请求模块是单进程单线程,所以不用考虑并发问题,其余处理模块可能是多线程的
方案1:利用setNX若存在则不插入,不存在则插入成功,同时value为时间戳,没拿到锁则判断时间是否过期,get拿到时间戳,过期则用,getset方式赋值,在比较时间戳是否过期,过期则拿到锁;
@Component
public class RedisLock {
@Autowired
StringRedisTemplate stringRedisTemplate;
public boolean addLock(String key, long expireTime) throws InterruptedException {
boolean lock = true;
long now = System.currentTimeMillis();
//setNx
while (!stringRedisTemplate.opsForValue().setIfAbsent(key, expireTime + "")) {
String lastTime = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.isNotEmpty(lastTime) && Long.parseLong(lastTime) < now) {
// getset
String recentTime = stringRedisTemplate.opsForValue().getAndSet(key, expireTime + "");
//必须判断上一次时间和当前取出来的时间一致才行
if (StringUtils.isNotEmpty(recentTime) && recentTime.equals(lastTime)) {
lock = true;
break;
}
}
System.out.println("等待...");
Thread.sleep(1000);
}
return lock;
}
public void unLock(String key, long nowTime) {
//加锁解锁同一个人
if (nowTime == Long.parseLong(stringRedisTemplate.opsForValue().get(key))) {
stringRedisTemplate.delete(key);
}
}
}
方案2:加锁:set(key,value,'NX','EX',expireTime)
解锁:eval(lua语句,key1,value)
//lua脚本:
if redis.call("get",KEYS[1]) == ARGV[1]
then
return redis.call("del",KEYS[1])
else
return 0
end
@Component
public class RedisFlusterLock {
@Autowired
StringRedisTemplate stringRedisTemplate;
private static final String LUA = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then "
+ " return redis.call(\"del\",KEYS[1]) " + "else " + " return 0 " + "end ";
public void tryLock(String key,String value, Long expireTime) {
while (1 == 1) {
String setResult = stringRedisTemplate.execute((RedisCallback<String>) connection -> {
Object nativeConnection = connection.getNativeConnection();
String result = null;
if (nativeConnection instanceof JedisCluster) {
result = ((JedisCluster) nativeConnection).set(key, value, "NX", "EX", expireTime);
}
if (nativeConnection instanceof Jedis) {
result = ((Jedis) nativeConnection).set(key, value, "NX", "EX", expireTime);
}
return result;
});
if ("ok".equalsIgnoreCase(setResult)) {
break;
} else {
try {
System.out.println("等待...");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public boolean unLock(String key, String value) {
if (stringRedisTemplate.opsForValue().get(key).equals(value)) {
Boolean delResult = stringRedisTemplate.execute((RedisCallback<Boolean>) connection -> {
Object nativeConnection = connection.getNativeConnection();
long result = 0L;
List<String> keys = new ArrayList<>();
keys.add(key);
List<String> values = new ArrayList<>();
values.add(value);
if (nativeConnection instanceof JedisCluster) {
result = (Long) ((JedisCluster) nativeConnection).eval(LUA, keys, values);
}
if (nativeConnection instanceof Jedis) {
result = (Long) ((Jedis) nativeConnection).eval(LUA, keys, values);
}
return result == 1L;
});
return delResult;
} else {
return false;
}
}
}
java实现参考: https://blog.csdn.net/xxs77ch/article/details/79133311
redis官方解释:https://redis.io/commands/set