Java實現Redis分散式鎖
阿新 • • 發佈:2019-01-30
1、背景:
在多執行緒環境下,通常會使用鎖來保證有且只有一個執行緒來操作共享資源。比如:
object obj = new object();lock (obj)
{
//操作共享資源
}
利用作業系統提供的鎖機制,可以確保多執行緒或多程序下的併發唯一操作。但如果在多機環境下就不能滿足了,當A,B兩臺機器同時操作C機器的共享資源時,就需要第三方的鎖機制來保證在分散式環境下的資源協調,也稱分散式鎖。
在分散式環境下(多節點主機)加鎖處理場景,如:秒殺、全域性遞增ID等等,需要對資源(變數)同步互斥。大部分的解決方案是基於DB實現的,Redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶端對Redis的連線並不存在競爭關係。由於Redis是單執行緒模型,命令操作原子性,所以利用這個特性可以很容易的實現分散式鎖。
2、Redis有三個最基本屬性來保證分散式鎖的有效實現:
安全性: 互斥,在任何時候,只有一個客戶端能持有鎖。
活躍性A:沒有死鎖,即使客戶端在持有鎖的時候崩潰,最後也會有其他客戶端能獲得鎖,超時機制。
活躍性B:故障容忍,只有大多數Redis節點時存活的,客戶端仍可以獲得鎖和釋放鎖。
使用Redis實現分散式鎖,有兩個重要函式需要介紹
1)SETNX命令(SET if Not eXists)
語法:
SETNX key value
功能:
當且僅當 key 不存在,將 key 的值設為 value ,並返回1;若給定的 key 已經存在,則 SETNX 不做任何動作,並返回0。
2)GETSET命令
語法:
GETSET key value
功能:
將給定 key 的值設為 value ,並返回 key 的舊值 (old value),當 key 存在但不是字串型別時,返回一個錯誤,當key不存在時,返回nil。
3、程式碼:
- package ct.tool;
- import redis.clients.jedis.Jedis;
- publicclass RedisDisLock {
- privatestaticfinallong expired = 1000;//1秒超時
- //上鎖
- publicstaticboolean acquireLock(Jedis jedis,String lock) {
- // 1. 通過SETNX試圖獲取一個lock
- boolean success = false;
-
long value = System.currentTimeMillis() + expired +
- long acquired = jedis.setnx(lock, String.valueOf(value));
- jedis.expire(lock, 1);//設定1秒超時
- //SETNX成功,則成功獲取一個鎖
- if (acquired == 1) success = true;
- //SETNX失敗,說明鎖被其他客戶端保持,檢查其是否已經超時
- /*else {
- long oldValue = Long.valueOf(jedis.get(lock));
- if (oldValue < System.currentTimeMillis()) {//超時
- //獲取上一個鎖到期時間,並設定現在的鎖到期時間,
- //只有一個執行緒才能獲取上一個線上的設定時間,因為jedis.getSet是同步的
- String getValue = jedis.getSet(lock, String.valueOf(value));
- if (getValue !=null) {
- if (Long.valueOf(getValue) == oldValue)
- success = true;
- else success = false;// 已被其他程序捷足先登了
- }
- }else //未超時,則直接返回失敗
- success = false;
- } */
- return success;
- }
- //釋放鎖
- publicstaticvoid releaseLock(Jedis jedis,String lock) {
- //long current = System.currentTimeMillis();
- // 避免刪除非自己獲取得到的鎖
- //if (current < Long.valueOf(jedis.get(lock)))
- jedis.del(lock);
- }
- }
程式碼應用中,要共享的程式碼段前加:
- //枷鎖
- boolean lockFlag=true;
- while(lockFlag){//迴圈等待拿鎖
- if (RedisDisLock.acquireLock(jd,"o2o")) lockFlag=false;
- }
業務處理後,釋放:
- RedisDisLock.releaseLock(jd, "o2o");