Redis 4.0.2分布式锁的Java实现
生活随笔
收集整理的這篇文章主要介紹了
Redis 4.0.2分布式锁的Java实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
簡介 Redis分布式鎖算法有兩種,一種是單個Redis實例下的,一種是多個Redis實例的Redlock算法。 官方推薦Redlock算法,但是這個算法需要比較多的Redis實例而且是完全互相獨立,不存在主從復制或者其他集群協調機制的,所以不太適合小項目。 單Redis實例 原理 某個線程調用Redis命令 SET key value NX PX 30000。 這個命令的意思是,僅在不存在key的時候才能被執行成功(NX選項),并且這個key有一個30秒的自動失效時間(NX選項)。key的失效時間是一個調用線程獨占鎖的時間。這個key的value最好是一個隨機數,value在所有的調用線程中必須是唯一的。value是隨機數主要是為了更安全的釋放鎖,釋放鎖的時候使用腳本告訴Redis,只有key存在并且存儲的值和我指定的值一樣才能告訴我刪除成功。可以通過以下Lua腳本實現: if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end。 使用這種方式釋放鎖可以避免刪除別的調用線程獲取成功的鎖。舉個例子:線程A取得資源鎖,當線程A運行完畢其他操作后要釋放鎖時,原來的鎖早已超時并且被Redis自動釋放,并且在這期間資源鎖又被線程B再次獲取到。如果僅使用DEL命令將key刪除,那么這種情況就會把線程B的鎖給刪除掉。使用Lua腳本就不會存在這種情況,因為腳本僅會刪除value等于線程的value的key。 但是這個方法有個缺點,如果Redis服務器宕機,那么就會導致無法獲取鎖,后果就是無法執行后續方法。假如采用主從復制,因為Redis主從是異步的,就是master不會等待slave接收了數據再響應客戶端。考慮這種情景,線程A請求鎖,master寫入成功后發送給slave前master宕機,觸發故障轉移,slave升級為master,正好這時線程B又來獲取鎖并且成功,那么線程A和B都可以執行任務,所以用這種方式的話,寧愿不要有slave。不過這種屬于小概率錯誤,在一個保證永不宕機的環境下這個方式沒有任何問題。 示例 以下Demo類中的調用線程可以假設是分布在多個JVM進程中的線程,為了方便測試,共享數據也是設置到Redis中。 import redis.clients.jedis.Jedis; /** * 單實例Redis分布式鎖工具類 */ public class RedisDLUtils { /** * 加鎖 * * @param jedis * @param key * @param value * @param seconds * @return */ public static Boolean lock(Jedis jedis, String key, String value, int seconds) { String result = jedis.set(key, value, "NX", "EX", seconds); return (result != null) && ("OK".equals(result)); } /** * 釋放鎖 * * @param jedis * @param key * @param value * @return */ public static Boolean unlock(Jedis jedis, String key, String value) { if (value.equals(jedis.get(key))) { return jedis.del(key) == 1; } return false; } /** * 通過執行Lua腳本釋放鎖 * * @param jedis * @param key * @param value * @return */ public static Boolean unlockByLua(Jedis jedis, String key, String value) { String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then return redis.call(\"del\",KEYS[1]) else return 0 end"; long result = (long) jedis.eval(script, 1, key, value); return result == 1; } } import com.ice.util.RedisDLUtils; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; import java.util.UUID; import java.util.concurrent.CountDownLatch; public class Demo { public static void main(String[] args) throws Exception { JedisPoolConfig jedisPoolConfig = new JedisPoolConfig(); jedisPoolConfig.setMinIdle(10); jedisPoolConfig.setMaxIdle(50); jedisPoolConfig.setMaxTotal(150); JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.164.128", 6379); CountDownLatch countDownLatch = new CountDownLatch(100); for (int i = 0; i < 100; i++) { new Thread(() -> { String uuid = UUID.randomUUID().toString(); String key = "lock"; Jedis jedis = jedisPool.getResource(); try { boolean result = false; do { result = RedisDLUtils.lock(jedis, key, uuid, 30); } while (result == false); int value = Integer.parseInt(jedis.get("value")); jedis.set("value", ++value + ""); } finally { jedis.close(); // RedisDLUtils.unlock(jedis, key, uuid); RedisDLUtils.unlockByLua(jedis, key, uuid); countDownLatch.countDown(); } }).start(); } countDownLatch.await(); try (Jedis jedis = new Jedis("192.168.164.128", 6379)) { int value = Integer.parseInt(jedis.get("value")); System.out.println("value=" + value); } } } Redlock算法
轉載于:https://www.cnblogs.com/gjb724332682/p/8609677.html
總結
以上是生活随笔為你收集整理的Redis 4.0.2分布式锁的Java实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: spring入门(一)
- 下一篇: web项目开启日志打印