大致思路:
加锁:
利用set方法的nx,px参数:
nx参数表示当key不存在时才成功,key存在时失败,故保证了排他性.
px参数表示设置过期时间,避免线程因出现异常而无法释放锁(删除key)的问题
释放锁:
不能简单地删除键,因为可能出现这样的情况:线程成功set拿到锁,由于执行时间过长,锁已经过期了,锁又被另一个线程拿到,这时该线程准备释放锁,可是锁已经不属于它了,所以不能让它随便删除key。只有当锁属于它的时候,才能让它删除锁.我们可以利用value标识锁的主人.
.我使用两个lua脚本实现加锁以及释放锁
redis_lock.lua
local key=KEYS[1]; --获取lock的键
local value=KEYS[2] --标识锁的主人
local expireTime=tonumber(ARGV[1]); --获取lock过期时间
local result=tonumber(redis.call("SETNX",KEYS[1],value));
if result==1
then
redis.call("EXPIRE", key,expireTime);
end
return result
redis_unlock.lua
local key=KEYS[1]; --删除锁的键
local value=KEYS[2]; --标识谁来删除
local owner=redis.call("GET",key); --锁的主人
if owner ==value --当锁属于自己,可以删除
then
redis.call("DEL",key);
return 1;
else
return 0;
end
RedisDistributedLock.java
package ink.toppest.secondskill.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;
/**
* 描述:
*
* @author HASEE
* @create 2018-11-13 20:34
*/
@Component
public class RedisDistributedLock {
@Autowired
StringRedisTemplate stringRedisTemplate;
//自旋获取锁,为了降低cpu损耗,睡眠一段时间
public void lock(String lock,String owner){
while (!ScriptUtil.readAndExecute("redis_lock.lua",stringRedisTemplate,
new ArrayList<String>(){
{
add(lock);
add(owner);
}
},"10")){
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//尝试获取锁,超过时间退出
public boolean tryLock(String lock,String owner,long time,TimeUnit timeUnit){
final long endTime=timeUnit.toMillis(time)+System.currentTimeMillis();
boolean result=false;
while(!(result=ScriptUtil.readAndExecute("redis_lock.lua",stringRedisTemplate,
new ArrayList<String>(){
{
add(lock);
add(owner);
}
},"300")) && endTime-System.currentTimeMillis()>=0){
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return result;
}
//释放锁
public boolean unlock(String lock,String owner){
return ScriptUtil.readAndExecute("redis_unlock.lua",stringRedisTemplate,
new ArrayList<String>(){
{
add(lock);
add(owner);
}
});
}
}