大致思路:

加锁:

利用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);
                    }
                });
    }
}

 

01-01 13:20