Redisson之lock()和tryLock()的區(qū)別及說明
lock()和tryLock()的區(qū)別和原理解析
在Redisson中 lock() 方法 與 tryLock() 方法是有區(qū)別的!
我們先來闡述兩者的區(qū)別,再分析它們的源碼。
lock() 與 tryLock() 的區(qū)別
(1)返回值: lock() 是沒有返回值的;tryLock() 的返回值是 boolean。
(2)時(shí)機(jī):lock() 一直等鎖釋放;tryLock() 獲取到鎖返回true,獲取不到鎖并直接返回false。
(3)tryLock() 是可以被打斷的,被中斷的;lock是不可以。
tryLock()
@Override public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { // 轉(zhuǎn)成毫秒,后面都是以毫秒為單位 long time = unit.toMillis(waitTime); // 當(dāng)前時(shí)間 long current = System.currentTimeMillis(); // 線程ID-線程標(biāo)識 long threadId = Thread.currentThread().getId(); // 嘗試獲取鎖 tryAcquire() !!! Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId); // 如果上面嘗試獲取鎖返回的是null,表示成功;如果返回的是時(shí)間則表示失敗。 if (ttl == null) { return true; } // 剩余等待時(shí)間 = 最大等待時(shí)間 -(用現(xiàn)在時(shí)間 - 獲取鎖前的時(shí)間) time -= System.currentTimeMillis() - current; // 剩余等待時(shí)間 < 0 失敗 if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } // 再次獲取當(dāng)前時(shí)間 current = System.currentTimeMillis(); // 重試邏輯,但不是簡單的直接重試! // subscribe是訂閱的意思 RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId); // 如果在剩余等待時(shí)間內(nèi),收到了釋放鎖那邊發(fā)過來的publish,則才會再次嘗試獲取鎖 if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) { if (!subscribeFuture.cancel(false)) { subscribeFuture.onComplete((res, e) -> { if (e == null) { // 取消訂閱 unsubscribe(subscribeFuture, threadId); } }); } // 獲取鎖失敗 acquireFailed(waitTime, unit, threadId); return false; } try { // 又重新計(jì)算了一下,上述的等待時(shí)間 time -= System.currentTimeMillis() - current; if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } // 重試! while (true) { long currentTime = System.currentTimeMillis(); ttl = tryAcquire(waitTime, leaseTime, unit, threadId); // 成功 if (ttl == null) { return true; } // 又獲取鎖失敗,再次計(jì)算上面的耗時(shí) time -= System.currentTimeMillis() - currentTime; if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } currentTime = System.currentTimeMillis(); // 采用信號量的方式重試! if (ttl >= 0 && ttl < time) { subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } else { subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS); } // 重新計(jì)算時(shí)間(充足就繼續(xù)循環(huán)) time -= System.currentTimeMillis() - currentTime; if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } } } finally { unsubscribe(subscribeFuture, threadId); } }
lock()
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException { // 獲取當(dāng)前線程 ID long threadId = Thread.currentThread().getId(); // 獲取鎖,正常獲取鎖則ttl為null,競爭鎖時(shí)返回鎖的過期時(shí)間 Long ttl = tryAcquire(-1, leaseTime, unit, threadId); if (ttl == null) { return; } // 訂閱鎖釋放事件 // 如果當(dāng)前線程通過 Redis 的 channel 訂閱鎖的釋放事件獲取得知已經(jīng)被釋放,則會發(fā)消息通知待等待的線程進(jìn)行競爭 RFuture<RedissonLockEntry> future = subscribe(threadId); if (interruptibly) { commandExecutor.syncSubscriptionInterrupted(future); } else { commandExecutor.syncSubscription(future); } try { while (true) { // 循環(huán)重試獲取鎖,直至重新獲取鎖成功才跳出循環(huán) // 此種做法阻塞進(jìn)程,一直處于等待鎖手動(dòng)釋放或者超時(shí)才繼續(xù)線程 ttl = tryAcquire(-1, leaseTime, unit, threadId); if (ttl == null) { break; } if (ttl >= 0) { try { future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { if (interruptibly) { throw e; } future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); } } else { if (interruptibly) { future.getNow().getLatch().acquire(); } else { future.getNow().getLatch().acquireUninterruptibly(); } } } } finally { // 最后釋放訂閱事件 unsubscribe(future, threadId); } }
建議應(yīng)盡量使用tryLock(),且攜帶參數(shù),因?yàn)榭稍O(shè)置最大等待時(shí)間以及可及時(shí)獲取加鎖返回值,后續(xù)可做一些其他加鎖失敗的業(yè)務(wù)。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
redis的五大數(shù)據(jù)類型應(yīng)用場景分析
這篇文章主要介紹了redis的五大數(shù)據(jù)類型實(shí)現(xiàn)原理,本文給大家分享五大數(shù)據(jù)類型的應(yīng)用場景分析,需要的朋友可以參考下2021-08-08Redis 2.8-4.0過期鍵優(yōu)化過程全紀(jì)錄
這篇文章主要給大家介紹了關(guān)于Redis 2.8-4.0過期鍵優(yōu)化的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04Redis實(shí)現(xiàn)編碼生成規(guī)則方式
在自動(dòng)生成編碼時(shí)應(yīng)采用“MD+年月日+4位序列號”的規(guī)則,如“MD202310130001”,為避免使用隨機(jī)序列號導(dǎo)致的重復(fù)編碼,建議使用從0開始的自增序列號,此外,使用Redis的incrBy功能實(shí)現(xiàn)序列號自增,可以有效提高效率和降低實(shí)現(xiàn)難度2023-01-01