1. 程式人生 > >使用redis實現分散式鎖

使用redis實現分散式鎖

大致思路:

加鎖:

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