Redisson解锁失败,watchdog会不会一直续期下去?
不用担心这个问题。在解锁过程中,无论解锁失败还是抛出异常,都会停止本地的续期任务,防止后续自动续期。具体实现逻辑如下:
@Override
public void unlock() {try {get(unlockAsync(Thread.currentThread().getId()));} catch (RedisException e) {if (e.getCause() instanceof IllegalMonitorStateException) {throw (IllegalMonitorStateException) e.getCause();} else {throw e;}}
}
这段代码是Redisson解锁方法的入口,会调用unlockAsync方法并传入当前线程ID:
@Override
public RFuture<Void> unlockAsync(long threadId) {return getServiceManager().execute(() -> unlockAsync0(threadId));
}private RFuture<Void> unlockAsync0(long threadId) {CompletionStage<Boolean> future = unlockInnerAsync(threadId);CompletionStage<Void> f = future.handle((opStatus, e) -> {cancelExpirationRenewal(threadId);if (e != null) {if (e instanceof CompletionException) {throw (CompletionException) e;}throw new CompletionException(e);}if (opStatus == null) {IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "+ id + " thread-id: " + threadId);throw new CompletionException(cause);}return null;});return new CompletableFutureWrapper<>(f);
}
核心逻辑包含两个关键步骤:
- 执行解锁操作:
unlockInnerAsync(threadId) - 处理解锁结果:通过
CompletionStage的handle方法执行后续操作
CompletionStage是Java 8引入的异步编程接口,它的handle方法能够处理操作的成功或失败状态。方法签名如下:
<T> CompletionStage<T> handle(BiFunction<? super T, Throwable, ? extends T> fn);
无论unlockInnerAsync过程中是否成功或出现异常,后续代码都会执行。其中关键部分是:
cancelExpirationRenewal(threadId);
这个方法的实现:
protected void cancelExpirationRenewal(Long threadId) {ExpirationEntry task = EXPIRATION_RENEWAL_MAP.get(getEntryName());if (task == null) {return;}if (threadId != null) {task.removeThreadId(threadId);}if (threadId == null || task.hasNoThreads()) {Timeout timeout = task.getTimeout();if (timeout != null) {timeout.cancel();}EXPIRATION_RENEWAL_MAP.remove(getEntryName());}
}
该方法会从EXPIRATION_RENEWAL_MAP中移除当前线程。由于自动续期依赖于该Map,移除后就不会再续期了。除非从本地Map中移除key的操作也失败,但这种概率极低。
