久久精品人人爽,华人av在线,亚洲性视频网站,欧美专区一二三

Redis如何實現可重入鎖的設計

177次閱讀
沒有評論

共計 3553 個字符,預計需要花費 9 分鐘才能閱讀完成。

自動寫代碼機器人,免費開通

這篇文章主要介紹 Redis 如何實現可重入鎖的設計,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

但是仍然有些場景是不滿?的,例如? 個?法獲取到鎖之后,可能在?法內調這個?法此時就獲取不到鎖了。這個時候我們就需要把鎖改進成可 重?鎖了。重?鎖,指的是以線程為單位,當?個線程獲取對象鎖之后,這個線程可以再次獲取本對象上的鎖,?其 他的線程是不可以的。可重?鎖的意義在于防?死鎖。實現原理是通過為每個鎖關聯?個請求計數器和?個占有它的線程。當計數為 0 時,認為鎖是未被占有 的;線程請求?個未被占有的鎖時,JVM 將記錄鎖的占有者,并且將請求計數器置為 1。如果同?個線程再次請求這個鎖,計數將遞增;每次占?線程退出同步塊,計數器值將遞減。直到計數器 為 0, 鎖被釋放。關于?類和?類的鎖的重?:?類覆寫了?類的 synchonized ?法,然后調??類中的?法,此時如果沒有重?的鎖,那么這段代碼將產?死鎖。

代碼演示

不可重?

不可重?鎖
Redis 如何實現可重入鎖的設計

使用不可重入鎖 Redis 如何實現可重入鎖的設計
當前線程執? call() ?法?先獲取 lock,接下來執? inc() ?法就?法執? inc() 中的邏輯,必須先釋放鎖。該例很好的說明了不可重?鎖。

可重入鎖

鎖實現
Redis 如何實現可重入鎖的設計

鎖使用 Redis 如何實現可重入鎖的設計

可重?意味著線程可進?它已經擁有的鎖的同步代碼塊。

設計兩個線程調? call() ?法,第?個線程調? call() ?法獲取鎖,進? lock() ?法,由于初始 lockedBy 是 null,所以不會進? while ?掛起當前線程,?是增量 lockedCount 并記錄 lockBy 為第 ?個線程。

接著第?個線程進? inc() ?法,由于同?進程,所以不會進? while ?掛起,接著增量 lockedCount,當第?個線程嘗試 lock,由于 isLocked=true, 所以他不會獲取該鎖,直到第?個線程調?兩次 unlock() 將 lockCount 遞減為 0,才將標記為 isLocked 設置為 false。

設計思路

假設鎖的 key 為“lock”,hashKey 是當前線程的 id:“threadId”,鎖自動釋放時間假設為 20。

獲取鎖

判斷 lock 是否存在 EXISTS lock

不存在,則自己獲取鎖,記錄重入層數為 1.

存在,說明有人獲取鎖了,繼續判斷是不是自己的鎖,即判斷當前線程 id 作為 hashKey 是否存在:HEXISTS lock threadId

不存在,說明鎖已經有了,且不是自己獲取的,鎖獲取失敗.

存在,說明是自己獲取的鎖,重入次數 +1:HINCRBY lock threadId 1,最后更新鎖自動釋放時間,EXPIRE lock 20
Redis 如何實現可重入鎖的設計

釋放鎖

判斷當前線程 id 作為 hashKey 是否存在:HEXISTS lock threadId

不存在,說明鎖已失效

存在,說明鎖還在,重入次數減 1:HINCRBY lock threadId -1,

獲取新的重入次數,判斷重入次數是否為 0,為 0 說明鎖全部釋放,刪除 key:DEL lock

因此,存儲在鎖中的信息就必須包含:key、線程標識、重入次數。不能再使用簡單的 key-value 結構,這里推薦使用 hash 結構。而且要讓所有指令都在同一個線程中操作,那么使用 lua 腳本。

lua 腳本

lock.lua

local key = KEYS[1]; --  第 1 個參數, 鎖的 keylocal threadId = ARGV[1]; --  第 2 個參數, 線程唯一標識 local releaseTime = ARGV[2]; --  第 3 個參數, 鎖的自動釋放時間 if(redis.call( exists , key) == 0) then --  判斷鎖是否已存在
 redis.call( hset , key, threadId,  1  --  不存在,  則獲取鎖
 redis.call(expire , key, releaseTime); --  設置有效期
 return 1; --  返回結果 end;if(redis.call( hexists , key, threadId) == 1) then --  鎖已經存在,判斷 threadId 是否是自己  
 redis.call( hincrby , key, threadId,  1  --  如果是自己,則重入次數 +1
 redis.call(expire , key, releaseTime); --  設置有效期
 return 1; --  返回結果 end;return 0; --  代碼走到這里, 說明獲取鎖的不是自己,獲取鎖失敗 

unlock.lua

--  鎖的  keylocal key = KEYS[1];--  線程唯一標識 local threadId = ARGV[1];--  判斷當前鎖是否還是被自己持有 if (redis.call( hexists , key, threadId) == 0) then--  如果已經不是自己,則直接返回
 return nil;end;--  是自己的鎖,則重入次數減一 local count = redis.call(hincrby , key, threadId, -1);--  判斷重入次數是否已為 0if (count == 0) then--  等于  0,說明可以釋放鎖,直接刪除
 redis.call(del , key);
 return nil;end;

在項目中集成

編寫 RedisLock 類

@Getter@Setterpublic class RedisLock {
 private RedisTemplate redisTemplate;
 private DefaultRedisScript Long  lockScript;
 private DefaultRedisScript Object  unlockScript;
 public RedisLock(RedisTemplate redisTemplate) {
 this.redisTemplate = redisTemplate;
 //  加載釋放鎖的腳本
 this.lockScript = new DefaultRedisScript ();
 this.lockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource( lock.lua)));
 this.lockScript.setResultType(Long.class);
 //  加載釋放鎖的腳本
 this.unlockScript = new DefaultRedisScript ();
 this.unlockScript.setScriptSource(new ResourceScriptSource(new ClassPathResource( unlock.lua)));
 }
 /**
 *  獲取鎖
 * @param lockName  鎖名稱
 * @param releaseTime  超時時間 (單位: 秒)
 * @return key  解鎖標識
 */
 public String tryLock(String lockName, long releaseTime) {
 //  存入的線程信息的前綴,防止與其它 JVM 中線程信息沖突
 String key = UUID.randomUUID().toString();
 //  執行腳本
 Long result = (Long)redisTemplate.execute(
 lockScript,
 Collections.singletonList(lockName),
 key + Thread.currentThread().getId(), releaseTime);
 //  判斷結果
 if(result != null   result.intValue() == 1) {
 return key;
 }else {
 return null;
 }
 }
 /**
 *  釋放鎖
 * @param lockName  鎖名稱
 * @param key  解鎖標識
 */
 public void unlock(String lockName, String key) {
 //  執行腳本
 redisTemplate.execute(
 unlockScript,
 Collections.singletonList(lockName),
 key + Thread.currentThread().getId(), null);
 }}

以上是“Redis 如何實現可重入鎖的設計”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注丸趣 TV 行業資訊頻道!

向 AI 問一下細節

正文完
 
丸趣
版權聲明:本站原創文章,由 丸趣 2023-12-04發表,共計3553字。
轉載說明:除特殊說明外本站除技術相關以外文章皆由網絡搜集發布,轉載請注明出處。
評論(沒有評論)
主站蜘蛛池模板: 高密市| 上高县| 木里| 昌黎县| 沧州市| 吉隆县| 株洲市| 克什克腾旗| 新民市| 台安县| 彝良县| 凌海市| 江口县| 长兴县| 新源县| 和林格尔县| 怀安县| 平潭县| 屏东市| 西畴县| 武邑县| 砚山县| 金川县| 泾源县| 陕西省| 五原县| 桦甸市| 诏安县| 江西省| 盘山县| 青海省| 嫩江县| 镶黄旗| 巴林右旗| 新沂市| 天柱县| 博爱县| 巩留县| 龙南县| 于田县| 鄂托克前旗|