您好,登錄后才能下訂單哦!
這篇“Java分布式鎖如何實現”文章的知識點大部分人都不太理解,所以小編給大家總結了以下內容,內容詳細,步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Java分布式鎖如何實現”文章吧。
單機多線程: 在 Java 中,我們通常使用 ReetrantLock 類、synchronized 關鍵字這類 本地鎖 來控制一個 JVM 進程內的多個線程對本地共享資源的訪問
分布式系統: 不同的服務/客戶端通常運行在獨立的 JVM 進程上。如果多個 JVM 進程共享同一份資源的話,使用本地鎖就沒辦法實現資源的互斥訪問了。于是,分布式鎖就誕生了。
舉個例子:系統的訂單服務一共部署了 3 份,都對外提供服務。用戶下訂單之前需要檢查庫存,為了防止超賣,這里需要加鎖以實現對檢查庫存操作的同步訪問。由于訂單服務位于不同的 JVM 進程中,本地鎖在這種情況下就沒辦法正常工作了。我們需要用到分布式鎖,這樣的話,即使多個線程不在同一個 JVM 進程中也能獲取到同一把鎖,進而實現共享資源的互斥訪問。
一個最基本的分布式鎖需要滿足:
互斥 :任意一個時刻,鎖只能被一個線程持有;
高可用 :鎖服務是高可用的。并且,即使客戶端的釋放鎖的代碼邏輯出現問題,鎖最終一定還是會被釋放,不會影響其他線程對共享資源的訪問。
可重入:一個節點獲取了鎖之后,還可以再次獲取鎖。
不論是本地鎖還是分布式鎖,核心都在于==“互斥”==。
在 Redis 中, SETNX
命令是可以幫助我們實現互斥。SETNX
即 SET if Not eXists (對應 Java 中的 setIfAbsent 方法),如果 key 不存在的話,才會設置 key 的值。如果 key 已經存在, SETNX 啥也不做。
> SETNX lockKey uniqueValue
(integer) 1
> SETNX lockKey uniqueValue
(integer) 0
釋放鎖的話,直接通過 DEL 命令刪除對應的 key 即可
> DEL lockKey
(integer) 1
為了防止誤刪到其他的鎖,這里我們建議使用 Lua 腳本通過 key 對應的 value(唯一值)來判斷。
選用 Lua 腳本是為了保證解鎖操作的原子性。因為 Redis 在執行 Lua 腳本時,可以以原子性的方式執行,從而保證了鎖釋放操作的原子性。
// 釋放鎖時,先比較鎖對應的 value 值是否相等,避免鎖的誤釋放 if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end
這是一種最簡易的 Redis 分布式鎖實現,實現方式比較簡單,性能也很高效。不過,這種方式實現分布式鎖存在一些問題。就比如應用程序遇到一些問題比如釋放鎖的邏輯突然掛掉,可能會導致鎖無法被釋放,進而造成共享資源無法再被其他線程/進程訪問。
主要為了避免鎖無法被釋放
127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK
lockKey :加鎖的鎖名;
uniqueValue :能夠唯一標示鎖的隨機字符串;
NX :只有當 lockKey 對應的 key 值不存在的時候才能 SET 成功;
EX :過期時間設置(秒為單位)EX 3 標示這個鎖有一個 3 秒的自動過期時間。與 EX 對應的是 PX(毫秒為單位),這兩個都是過期時間設置。
一定要保證設置指定 key 的值和過期時間是一個原子操作!!! 不然的話,依然可能會出現鎖無法被釋放的問題。
這種解決辦法同樣存在漏洞:
如果操作共享資源的時間大于過期時間,就會出現鎖提前過期的問題,進而導致分布式鎖直接失效
如果鎖的超時時間設置過長,又會影響到性能
Redisson 是一個開源的 Java 語言 Redis 客戶端,提供了很多開箱即用的功能,不僅僅包括多種分布式鎖的實現。并且,Redisson 還支持 Redis 單機、Redis Sentinel 、Redis Cluster 等多種部署架構。
Redisson 中的分布式鎖自帶自動續期機制,使用起來非常簡單,原理也比較簡單,其提供了一個專門用來監控和續期鎖的 Watch Dog( 看門狗),如果操作共享資源的線程還未執行完成的話,Watch Dog 會不斷地延長鎖的過期時間,進而保證鎖不會因為超時而被釋放。
使用方式舉例:
// 1.獲取指定的分布式鎖對象
RLock lock = redisson.getLock("lock");
// 2.拿鎖且不設置鎖超時時間,具備 Watch Dog 自動續期機制
lock.lock();
// 3.執行業務
...
// 4.釋放鎖
lock.unlock();
只有未指定鎖超時時間,才會使用到 Watch Dog 自動續期機制。
// 手動給鎖設置過期時間,不具備 Watch Dog 自動續期機制 lock.lock(10, TimeUnit.SECONDS);
總的來說就是使用Redisson,它帶有自動的續期機制
所謂可重入鎖指的是在一個線程中可以多次獲取同一把鎖,比如一個線程在執行一個帶鎖的方法,該方法中又調用了另一個需要相同鎖的方法,則該線程可以直接執行調用的方法即可重入 ,而無需重新獲得鎖。像 Java 中的 synchronized 和 ReentrantLock 都屬于可重入鎖。
可重入分布式鎖的實現核心思路是線程在獲取鎖的時候判斷是否為自己的鎖,如果是的話,就不用再重新獲取了。為此,我們可以為每個鎖關聯一個可重入計數器和一個占有它的線程。當可重入計數器大于 0 時,則鎖被占有,需要判斷占有該鎖的線程和請求獲取鎖的線程是否為同一個。
以上就是關于“Java分布式鎖如何實現”這篇文章的內容,相信大家都有了一定的了解,希望小編分享的內容對大家有幫助,若想了解更多相關的知識內容,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。