Redisson之lock()和tryLock()的區(qū)別及說(shuō)明
lock()和tryLock()的區(qū)別和原理解析
在Redisson中 lock() 方法 與 tryLock() 方法是有區(qū)別的!
我們先來(lái)闡述兩者的區(qū)別,再分析它們的源碼。

lock() 與 tryLock() 的區(qū)別
(1)返回值: lock() 是沒(méi)有返回值的;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)識(shí)
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();
// 重試邏輯,但不是簡(jiǎn)單的直接重試!
// subscribe是訂閱的意思
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
// 如果在剩余等待時(shí)間內(nèi),收到了釋放鎖那邊發(fā)過(guò)來(lái)的publish,則才會(huì)再次嘗試獲取鎖
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();
// 采用信號(hào)量的方式重試!
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,競(jìng)爭(zhēng)鎖時(shí)返回鎖的過(guò)期時(shí)間
Long ttl = tryAcquire(-1, leaseTime, unit, threadId);
if (ttl == null) {
return;
}
// 訂閱鎖釋放事件
// 如果當(dāng)前線程通過(guò) Redis 的 channel 訂閱鎖的釋放事件獲取得知已經(jīng)被釋放,則會(huì)發(fā)消息通知待等待的線程進(jìn)行競(jìng)爭(zhēng)
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中使用bloomfilter的白名單功能解決緩存穿透問(wèn)題
本文主要介紹了redis中使用bloomfilter的白名單功能解決緩存穿透問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07
Redis中緩存和數(shù)據(jù)庫(kù)雙寫(xiě)數(shù)據(jù)不一致的原因及解決方案
這篇文章主要介紹了Redis中緩存和數(shù)據(jù)庫(kù)雙寫(xiě)數(shù)據(jù)不一致的原因及解決方案,文中通過(guò)圖文結(jié)合的方式講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2024-03-03
Redis實(shí)現(xiàn)分布式事務(wù)的示例
Redis雖不支持傳統(tǒng)SQL數(shù)據(jù)庫(kù)ACID特性的事務(wù),但提供了事務(wù)特性,允許多命令捆綁執(zhí)行,通過(guò)命令MULTI、EXEC、DISCARD、WATCH實(shí)現(xiàn),感興趣的可以了解一下2024-10-10
Redis客戶端連接遠(yuǎn)程Redis服務(wù)器方式
這篇文章主要介紹了Redis客戶端連接遠(yuǎn)程Redis服務(wù)器方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解
在本篇文章里小編給大家整理了一篇關(guān)于Redis實(shí)現(xiàn)分布式鎖的實(shí)例講解內(nèi)容,有興趣的朋友們可以學(xué)習(xí)參考下。2021-12-12
Redis實(shí)現(xiàn)高效內(nèi)存管理的示例代碼
Redis內(nèi)存管理是其核心功能之一,為了高效地利用內(nèi)存,Redis采用了多種技術(shù)和策略,如優(yōu)化的數(shù)據(jù)結(jié)構(gòu)、內(nèi)存分配策略、內(nèi)存回收、數(shù)據(jù)壓縮等,下面就來(lái)詳細(xì)的介紹一下2025-08-08

