您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關Java基于Redis如何實現分布式鎖的內容。小編覺得挺實用的,因此分享給大家做個參考。一起跟隨小編過來看看吧。
分布式鎖可以基于很多種方式實現,比如zookeeper、redis...。不管哪種方式,他的基本原理是不變的:用一個狀態值表示鎖,對鎖的占用和釋放通過狀態值來標識。
一、為什么Redis可以方便地實現分布式鎖
1、Redis為單進程單線程模式,采用隊列模式將并發訪問變成串行訪問,且多客戶端對Redis的連接并不存在競爭關系。
2、Redis的SETNX命令可以方便的實現分布式鎖。
setNX(SET if Not eXists)
語法:SETNX key value
返回值:設置成功,返回 1 ;設置失敗,返回 0 。
當且僅當 key 不存在時將 key 的值設為 value,并返回1;若給定的 key 已經存在,則 SETNX 不做任何動作,并返回0。
綜上所述,可以通過setnx的返回值來判斷是否獲取到鎖,并且不用擔心并發訪問的問題,因為Redis是單線程的,所以如果返回1則獲取到鎖,返回0則沒獲取到。當業務操作執行完后,一定要釋放鎖,釋放鎖的邏輯很簡單,就是把之前設置的key刪除掉即可,這樣下次又可以通過setnx該key獲取到鎖了。
二、分布式鎖實現
我們已經知道可以通過Redis自帶的函數setNX來實現分布式鎖,具體實現步驟如下。
我在一臺CentOS7的linux虛擬機中安裝了Redis服務,ip地址為:192.168.246.130,服務端口為:6379。
下面是java通過redis實現分布式鎖的例子:
import redis.clients.jedis.Jedis; public class RedisLock { //鎖的key private static final String key = "DistributedRedisLock"; private static Integer count = 0; public static void main(String[] args) { for(int i=0;i<1000;i++){ new Thread(new Runnable() { @Override public void run() { //獲取Redis連接 Jedis jedis = new Jedis("192.168.246.130", 6379); try{ while(true){ //獲取鎖 if(jedis.setnx(key, Thread.currentThread().getName()) == 1){ try{ System.out.println("線程("+Thread.currentThread().getName()+")獲取到鎖,開始執行操作"); count++; System.out.println(count); break; }finally{ System.out.println("操作執行完成,釋放鎖"); //操作執行完一定要釋放鎖,所以在finally塊中執行 jedis.del(key); } }else{ //返回的不是1,說明已經有某個線程獲取到了鎖 try { //等待100毫秒之后重試 Thread.sleep(100l); } catch (InterruptedException e) { e.printStackTrace(); } } } }catch(Exception e){ e.printStackTrace(); }finally{ //釋放Redis連接 jedis.disconnect(); } } }).start(); } } }
上述代碼的輸出結果為:
線程(Thread-320)獲取到鎖,開始執行操作 1 操作執行完成,釋放鎖 線程(Thread-463)獲取到鎖,開始執行操作 2 操作執行完成,釋放鎖 線程(Thread-997)獲取到鎖,開始執行操作 3 操作執行完成,釋放鎖 ... 線程(Thread-409)獲取到鎖,開始執行操作 998 操作執行完成,釋放鎖 線程(Thread-742)獲取到鎖,開始執行操作 999 操作執行完成,釋放鎖 線程(Thread-286)獲取到鎖,開始執行操作 1000 操作執行完成,釋放鎖
上述代碼雖然是在單應用多線程情況下測試的,但即便是在分布式環境下多應用多線程去獲取鎖,結果依然是正確的。
三、解決死鎖問題
之前的例子代碼只是測試代碼,只是為了說明原理,例子本身很簡單,所以有一些考慮不周的地方。比如當獲取到鎖之后在業務操作執行過程中發生了環境問題導致斷開了和Redis的連接,那就無法在finally塊中釋放鎖,導致其他等待獲取鎖的線程無限等待下去,也就是發生了死鎖現象。
解決方式:
可以在Redis中給鎖設置一個過期時間,這樣即便無法釋放鎖,鎖也能在一段時間后自動釋放。
代碼上只需要在獲取到鎖之后在try語句塊中加入如下代碼:
jedis.expire(key, 10); //這里給鎖設置10秒的過期時間
更妥善的解決方式:
第一個解決方式并不是很好,因為當業務操作處理時間很長,超過了設置的過期時間,那鎖就自動釋放了,然后再執行finally塊中釋放鎖的操作時,這個鎖可能已經被其他線程所持有,會導致把其他線程持有的鎖給釋放了,從而導致并發問題。所以更妥善一點的方式是在釋放鎖時判斷一下鎖是否已經過期,如果已經過期就不用再釋放了。
代碼上把獲取到鎖之后的操作改為如下代碼:
long start = System.currentTimeMillis(); //獲取起始時間毫秒數 try{ jedis.expire(key, 10); ... }finally{ ... if(System.currentTimeMillis() < start+10*1000){ //如果之前設置的鎖還未過期,則釋放掉 jedis.del(key); } }
感謝各位的閱讀!關于Java基于Redis如何實現分布式鎖就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。