获取锁实现原理 使用redis lua脚本 lua脚本执行是个原子操作
重要命令 setnx 如果key不存在则设置值
代码:
/**
* 获取锁
* 原因是 redis是单线程的 一但一个线程获取锁后 其它线程就不能获取的锁
* @param lockKey 锁名称
* @param value 值
* @param expire 过期时间
* @param timeout 等待超时时间
* @return
*/
public boolean getLock(String lockKey,String value ,String expire, long timeout){
//获取当前毫米数
long startTime = System.currentTimeMillis();
try{
while (true){
String script = "if redis.call('setNx',KEYS[1],ARGV[1]) == 1 then " +
"if redis.call('get',KEYS[1]) == ARGV[1] then " +
"return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script,Long.class);
List<String> keys = new ArrayList<>();
keys.add(lockKey);
Long execute = redisTemplate.execute(redisScript, keys, value, expire);
if (1l == execute.longValue()){
return true;
} else {
long endTime = System.currentTimeMillis();
if ((endTime - startTime) > timeout){
return false;
}
}
}
}catch (Exception e){
e.printStackTrace();
return false;
}
}
释放锁:
/**
* 释放锁
* @param lockKey 锁
* @param value 值
* @return
*/
public boolean releaseLock(String lockKey,String value){
String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script,Long.class);
List<String> keys = new ArrayList<>();
keys.add(lockKey);
Long execute = redisTemplate.execute(redisScript, keys, value);
if (1l == execute.longValue()){
return true;
}
return false;
}
测试案例:
public void getLock(){
String key = "aLock";
String value = "1";
try {
boolean flag = redisUtil.getLock(key,value,"10",20000);
if (flag){
System.out.println("获取锁 处理业务逻辑");
} else {
System.out.println("获取锁 处理业务超时");
}
}finally {
boolean b = redisUtil.releaseLock(key, value);
if (b){
System.out.println("释放锁成功");
} else {
System.out.println("释放锁失败");
}
}
}
注意:expire 要为字符串类型 原因:当参数转换为byte数组的时候,会将非字符串转换为字符串时报转换异常。