在Redisson中 lock() 方法 与 tryLock() 方法是有区别的!
我们先来阐述两者的区别,再分析它们的源码。
lock() 与 tryLock() 的区别(1)返回值: lock() 是没有返回值的;tryLock() 的返回值是 boolean。
(2)时机:lock() 一直等锁释放;tryLock() 获取到锁返回true,获取不到锁并直接返回false。
(3)tryLock() 是可以被打断的,被中断的;lock是不可以。
- @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
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);
- }
- }
- 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
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(),且携带参数,因为可设置最大等待时间以及可及时获取加锁返回值,后续可做一些其他加锁失败的业务。