Redis 併發鎖實現

聽到Redis 實現併發鎖,大家應該都很熟悉了,不知道有多少同學踩過redis併發鎖的坑。

最近項目中有同學實現了併發鎖,通過代碼review還是發現有些同學理解的並不深入,爲後續的運行埋下來了巨大的隱患,今天空閒之餘再重溫一下併發鎖,希望多剛接觸redis 鎖的同學有啓發。

首先列舉一下幾種常見的寫法啊

1、第一種,也是在review代碼時經常遇到的

Jedis jedis;

public boolean tryLock(String key){

    String value = jedis.get(key);

    if(org.apache.commons.lang3.StringUtils.isEmpty(value)){

        jedis.set(key,"1");
        jedis.expire(key,1000);

        return true;
    }

    return false;

}
這種寫法有幾個問題呢?
1 String value = jedis.get(key);   多線程高併發訪問時,拿到的value都爲空都能拿到鎖,併發控制失效
2 jedis.set(key,"1") ;jedis.expire(key,1000); 非原子操作,設置失效時間失敗時,其他線程將永遠獲取不到鎖。造成死鎖。

2、第二種也是經常遇到的,還是先上代碼纔有說服力

    

Jedis jedis;

public boolean tryLock(String key){

    long value = jedis.incr(key);
    
    // 已經有其他線程獲取到鎖
    if(value != 1)
    {
      return false;    
    }else
    {
        jedis.expire(key,100);
        return true;
    }
    

}

這種寫法又有什麼問題呢?

首先和第一個有相似之處的是,設置redis失效時間不是原子操作,中間任何異常導致設置失效時間出錯時,會造成後續的死鎖

看了這幾個,有沒有踩過同樣的坑呢?

首先我認爲寫redis 併發鎖,有幾個堅持的原則

A、key和redis的失效時間是不是原子操作

B、get--》compare的方法在高併發場景寫不可取


3、三是我們最常用的,能滿足我們常規加鎖需求

Jedis jedis;

// 加鎖
public boolean tryLock(String key){

    String result = jedis.setex(key,1000,"1");


    if(StringUtils.equals("OK",result)){

        return true;
    }
 
    return false;
}


// 釋放鎖
public void  releaseLock(String key){
    
    jedis.del(key);
    
}
這裏的實現思路是客戶端進來加鎖、任務執行完畢後釋放鎖,看上去這種方式是完美的。但......我們redis 設置都是主備的,
Client A 在master上得到鎖,此時redis master Down機,redis將切換到Slave機器,Client B在此時去獲取鎖,
此時Client A的鎖信息還有同步到Slave,這種情況Client A、B 都將獲取到併發鎖。
對於這種情況大家還是多看看redis 官方對併發鎖的實現講解。java 版本的開源實現併發鎖Redisson
注:以上代碼均爲僞代碼,有問題大家可以留言討論,也希望文章對看到的人有些許用途 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章