這篇文章主要介紹redis如何實現(xiàn)可重入鎖的設(shè)計,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!
專注于為中小企業(yè)提供網(wǎng)站建設(shè)、成都網(wǎng)站制作服務(wù),電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)大竹免費做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了超過千家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設(shè)實現(xiàn)規(guī)模擴充和轉(zhuǎn)變。
但是仍然有些場景是不滿?的,例如? 個?法獲取到鎖之后,可能在?法內(nèi)調(diào)這個?法此時就獲取不到鎖了。這個時候我們就需要把鎖改進成可 重?鎖了。 重?鎖,指的是以線程為單位,當?個線程獲取對象鎖之后,這個線程可以再次獲取本對象上的鎖,?其 他的線程是不可以的。可重?鎖的意義在于防?死鎖。 實現(xiàn)原理是通過為每個鎖關(guān)聯(lián)?個請求計數(shù)器和?個占有它的線程。當計數(shù)為 0 時,認為鎖是未被占有 的;線程請求?個未被占有的鎖時,JVM 將記錄鎖的占有者,并且將請求計數(shù)器置為 1 。 如果同?個線程再次請求這個鎖,計數(shù)將遞增;每次占?線程退出同步塊,計數(shù)器值將遞減。直到計數(shù)器 為 0, 鎖被釋放。 關(guān)于?類和?類的鎖的重?:?類覆寫了?類的 synchonized ?法,然后調(diào)??類中的?法,此時如果沒有重?的鎖,那么這段代碼將產(chǎn)?死鎖。
代碼演示
不可重?
不可重?鎖
使用不可重入鎖
當前線程執(zhí)? call() ?法?先獲取 lock,接下來執(zhí)? inc() ?法就?法執(zhí)? inc() 中的邏輯,必須先釋放鎖。該例很好的說明了不可重?鎖。
可重入鎖
鎖實現(xiàn)
鎖使用
可重?意味著線程可進?它已經(jīng)擁有的鎖的同步代碼塊。
設(shè)計兩個線程調(diào)? call() ?法,第?個線程調(diào)? call() ?法獲取鎖,進? lock() ?法,由于初始 lockedBy 是 null,所以不會進? while ?掛起當前線程,?是增量 lockedCount 并記錄 lockBy 為第 ?個線程。
接著第?個線程進? inc() ?法,由于同?進程,所以不會進? while ?掛起,接著增量 lockedCount,當?shù)?個線程嘗試 lock,由于 isLocked=true, 所以他不會獲取該鎖,直到第?個線程調(diào)?兩次 unlock() 將 lockCount 遞減為 0,才將標記為 isLocked 設(shè)置為 false。
設(shè)計思路
假設(shè)鎖的key為“l(fā)ock”,hashKey是當前線程的id:“threadId”,鎖自動釋放時間假設(shè)為20。
判斷l(xiāng)ock是否存在 EXISTS lock
不存在,則自己獲取鎖,記錄重入層數(shù)為1.
存在,說明有人獲取鎖了,繼續(xù)判斷是不是自己的鎖,即判斷當前線程id作為hashKey是否存在:HEXISTS lock threadId
不存在,說明鎖已經(jīng)有了,且不是自己獲取的,鎖獲取失敗.
存在,說明是自己獲取的鎖,重入次數(shù)+1: HINCRBY lock threadId 1 ,最后更新鎖自動釋放時間, EXPIRE lock 20
判斷當前線程id作為hashKey是否存在: HEXISTS lock threadId
不存在,說明鎖已失效
存在,說明鎖還在,重入次數(shù)減1: HINCRBY lock threadId -1 ,
獲取新的重入次數(shù),判斷重入次數(shù)是否為0,為0說明鎖全部釋放,刪除key: DEL lock
因此,存儲在鎖中的信息就必須包含:key、線程標識、重入次數(shù)。不能再使用簡單的 key-value 結(jié)構(gòu), 這里推薦使用 hash 結(jié)構(gòu)。而且要讓所有指令都在同一個線程中操作,那么使用 lua 腳本。
lua 腳本
lock.lua
local key = KEYS[1]; -- 第1個參數(shù),鎖的keylocal threadId = ARGV[1]; -- 第2個參數(shù),線程唯一標識local releaseTime = ARGV[2]; -- 第3個參數(shù),鎖的自動釋放時間if(redis.call('exists', key) == 0) then -- 判斷鎖是否已存在 redis.call('hset', key, threadId, '1'); -- 不存在, 則獲取鎖 redis.call('expire', key, releaseTime); -- 設(shè)置有效期 return 1; -- 返回結(jié)果end;if(redis.call('hexists', key, threadId) == 1) then -- 鎖已經(jīng)存在,判斷threadId是否是自己 redis.call('hincrby', key, threadId, '1'); -- 如果是自己,則重入次數(shù)+1 redis.call('expire', key, releaseTime); -- 設(shè)置有效期 return 1; -- 返回結(jié)果end;return 0; -- 代碼走到這里,說明獲取鎖的不是自己,獲取鎖失敗
unlock.lua
-- 鎖的 keylocal key = KEYS[1];-- 線程唯一標識local threadId = ARGV[1];-- 判斷當前鎖是否還是被自己持有if (redis.call('hexists', key, threadId) == 0) then-- 如果已經(jīng)不是自己,則直接返回 return nil;end;-- 是自己的鎖,則重入次數(shù)減一local count = redis.call('hincrby', key, threadId, -1);-- 判斷重入次數(shù)是否已為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(); // 執(zhí)行腳本 Long result = (Long)redisTemplate.execute( lockScript, Collections.singletonList(lockName), key + Thread.currentThread().getId(), releaseTime); // 判斷結(jié)果 if(result != null && result.intValue() == 1) { return key; }else { return null; } } /** * 釋放鎖 * @param lockName 鎖名稱 * @param key 解鎖標識 */ public void unlock(String lockName, String key) { // 執(zhí)行腳本 redisTemplate.execute( unlockScript, Collections.singletonList(lockName), key + Thread.currentThread().getId(), null); }}
以上是“Redis如何實現(xiàn)可重入鎖的設(shè)計”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對大家有幫助,更多相關(guān)知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!
文章名稱:Redis如何實現(xiàn)可重入鎖的設(shè)計
標題路徑:http://www.muchs.cn/article4/jpceie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供Google、網(wǎng)站設(shè)計、域名注冊、全網(wǎng)營銷推廣、網(wǎng)站收錄、網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)