借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同時有多個客戶端發送setnx命令,只有一個客戶端可以成功,返回1(true);其他的客戶端返回0(false)。
本教程操作環境:windows7系統、Redis5.0.10版、DELL G3電腦。
分布式鎖的實現
隨著業務發展的需要,原單體單機部署的系統被演化成分布式集群系統后,由于分布式系統多線程、多進程并且分布在不同機器上,這將使原單機部署情況下的并發控制鎖策略失效,單純的Java API并不能提供分布式鎖的能力。為了解決這個問題就需要一種跨JVM的互斥機制來控制共享資源的訪問,這就是分布式鎖要解決的問題!
分布式鎖主流的實現方案:
-
基于數據庫實現分布式鎖
-
基于緩存(Redis等)
-
基于Zookeeper
這里,我們就基于redis實現分布式鎖。
基本實現
借助于redis中的命令setnx(key, value),key不存在就新增,存在就什么都不做。同時有多個客戶端發送setnx命令,只有一個客戶端可以成功,返回1(true);其他的客戶端返回0(false)。
主要使用Redis Setnx 命令
在指定的 key 不存在時,為 key 設置指定的值
設置成功,返回 1 。 設置失敗,返回 0
redis>?EXISTS?job????????????????#?job?不存在 (integer)?0 ? redis>?SETNX?job?"programmer"????#?job?設置成功 (integer)?1 ? redis>?SETNX?job?"code-farmer"???#?嘗試覆蓋?job?,失敗 (integer)?0 ? redis>?GET?job???????????????????#?沒有被覆蓋 "programmer"
java代碼
public?void?testLock()?{ //?執行redis的setnx命令 String?uuid?=?UUID.randomUUID().toString(); Boolean?lock?=?redisTemplate.opsForValue().setIfAbsent("lock",?uuid,?5,?TimeUnit.SECONDS); ? //?判斷是否拿到鎖 if?(lock)?{ //?執行業務邏輯代碼 //?... ? //?釋放鎖資源?(保證獲取值和刪除操作的原子性)?LUA腳本保證刪除的原子性 String?script?=?"if?redis.call('get',?KEYS[1])?==?ARGV[1]?then ?return?redis.call('del',?KEYS[1])?else?return?0?end"; this.redisTemplate.execute(new?DefaultRedisScript(script),? Arrays.asList("lock"),?Arrays.asList(uuid)); // if?(StrUtil.equals(uuid,redisTemplate.opsForValue().get("lock"))){ // redisTemplate.delete("lock"); // } }?else?{ //?其他請求嘗試獲取鎖 testLock(); } }
為了確保分布式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件:
互斥性。在任意時刻,只有一個客戶端能持有鎖。
不會發生死鎖。即使有一個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證后續其他客戶端能加鎖。
解鈴還須系鈴人。加鎖和解鎖必須是同一個客戶端,客戶端自己不能把別人加的鎖給解了。
相關教程推薦:Redis教程