Redisson之lock()和tryLock()的區(qū)別及說明
更新時間:2023年12月07日 10:02:43 作者:金鱗踏雨
這篇文章主要介紹了Redisson之lock()和tryLock()的區(qū)別及說明,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
lock()和tryLock()的區(qū)別和原理解析
在Redisson中 lock() 方法 與 tryLock() 方法是有區(qū)別的!
我們先來闡述兩者的區(qū)別,再分析它們的源碼。
lock() 與 tryLock() 的區(qū)別
(1)返回值: lock() 是沒有返回值的;tryLock() 的返回值是 boolean。
(2)時機:lock() 一直等鎖釋放;tryLock() 獲取到鎖返回true,獲取不到鎖并直接返回false。
(3)tryLock() 是可以被打斷的,被中斷的;lock是不可以。
tryLock()
@Override public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException { // 轉成毫秒,后面都是以毫秒為單位 long time = unit.toMillis(waitTime); // 當前時間 long current = System.currentTimeMillis(); // 線程ID-線程標識 long threadId = Thread.currentThread().getId(); // 嘗試獲取鎖 tryAcquire() !!! Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId); // 如果上面嘗試獲取鎖返回的是null,表示成功;如果返回的是時間則表示失敗。 if (ttl == null) { return true; } // 剩余等待時間 = 最大等待時間 -(用現(xiàn)在時間 - 獲取鎖前的時間) time -= System.currentTimeMillis() - current; // 剩余等待時間 < 0 失敗 if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } // 再次獲取當前時間 current = System.currentTimeMillis(); // 重試邏輯,但不是簡單的直接重試! // subscribe是訂閱的意思 RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId); // 如果在剩余等待時間內,收到了釋放鎖那邊發(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 { // 又重新計算了一下,上述的等待時間 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; } // 又獲取鎖失敗,再次計算上面的耗時 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); } // 重新計算時間(充足就繼續(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 { // 獲取當前線程 ID long threadId = Thread.currentThread().getId(); // 獲取鎖,正常獲取鎖則ttl為null,競爭鎖時返回鎖的過期時間 Long ttl = tryAcquire(-1, leaseTime, unit, threadId); if (ttl == null) { return; } // 訂閱鎖釋放事件 // 如果當前線程通過 Redis 的 channel 訂閱鎖的釋放事件獲取得知已經(jīng)被釋放,則會發(fā)消息通知待等待的線程進行競爭 RFuture<RedissonLockEntry> future = subscribe(threadId); if (interruptibly) { commandExecutor.syncSubscriptionInterrupted(future); } else { commandExecutor.syncSubscription(future); } try { while (true) { // 循環(huán)重試獲取鎖,直至重新獲取鎖成功才跳出循環(huán) // 此種做法阻塞進程,一直處于等待鎖手動釋放或者超時才繼續(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); } }
建議應盡量使用tryLock(),且攜帶參數(shù),因為可設置最大等待時間以及可及時獲取加鎖返回值,后續(xù)可做一些其他加鎖失敗的業(yè)務。
總結
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。