Redisson之lock()和tryLock()的区别及说明
作者:金鳞踏雨
这篇文章主要介绍了Redisson之lock()和tryLock()的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
lock()和tryLock()的区别和原理解析
在Redisson中 lock() 方法 与 tryLock() 方法是有区别的!
我们先来阐述两者的区别,再分析它们的源码。
lock() 与 tryLock() 的区别
(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; } // 剩余等待时间 = 最大等待时间 -(用现在时间 - 获取锁前的时间) time -= System.currentTimeMillis() - current; // 剩余等待时间 < 0 失败 if (time <= 0) { acquireFailed(waitTime, unit, threadId); return false; } // 再次获取当前时间 current = System.currentTimeMillis(); // 重试逻辑,但不是简单的直接重试! // subscribe是订阅的意思 RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId); // 如果在剩余等待时间内,收到了释放锁那边发过来的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); } // 重新计算时间(充足就继续循环) 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 订阅锁的释放事件获取得知已经被释放,则会发消息通知待等待的线程进行竞争 RFuture<RedissonLockEntry> future = subscribe(threadId); if (interruptibly) { commandExecutor.syncSubscriptionInterrupted(future); } else { commandExecutor.syncSubscription(future); } try { while (true) { // 循环重试获取锁,直至重新获取锁成功才跳出循环 // 此种做法阻塞进程,一直处于等待锁手动释放或者超时才继续线程 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(),且携带参数,因为可设置最大等待时间以及可及时获取加锁返回值,后续可做一些其他加锁失败的业务。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。