Redisson解锁失败,watchdog会不会一直续期下去?

不用担心这个问题。在解锁过程中,无论解锁失败还是抛出异常,都会停止本地的续期任务,防止后续自动续期。具体实现逻辑如下:

java 复制代码
@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:

java 复制代码
@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);
}

核心逻辑包含两个关键步骤:

  1. 执行解锁操作:unlockInnerAsync(threadId)
  2. 处理解锁结果:通过CompletionStagehandle方法执行后续操作

CompletionStage是Java 8引入的异步编程接口,它的handle方法能够处理操作的成功或失败状态。方法签名如下:

java 复制代码
<T> CompletionStage<T> handle(BiFunction<? super T, Throwable, ? extends T> fn);

无论unlockInnerAsync过程中是否成功或出现异常,后续代码都会执行。其中关键部分是:

java 复制代码
cancelExpirationRenewal(threadId);

这个方法的实现:

java 复制代码
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的操作也失败,但这种概率极低。

相关推荐
天衍四九-9 分钟前
MySQL有哪些索引
数据库·mysql
A-刘晨阳29 分钟前
K8s集群中的数据库新范式:KES-Operator解锁部署、扩缩容、备份全链路自动化
运维·数据库·云原生·kubernetes·自动化
载数而行52030 分钟前
redis集群
redis
大模型码小白41 分钟前
Harness Engineering 驾驭工程:从使用到项目实战
大数据·数据库·人工智能·python·spring
abcyuu1 小时前
福州中小企业业财一体化系统权限管理与数据安全实践方案
大数据·网络·数据库
灵境(虚幻知音)2 小时前
效能评估指标体系构建:方法、流程与模板
android·数据库
沈管家AI数字员工2 小时前
对话式数据分析实操:从自然语言到可视化图表的全流程
数据库·人工智能·ai·oracle·数据分析
FfHUCisI2 小时前
Go 内存分配器概览:从 TCMalloc 到三级缓存架构
缓存·架构·golang
may_一一2 小时前
MongoDB数据查看(Studio 3T)
数据库·mongodb
2401_833269303 小时前
RecyclerView的缓存复用机制
缓存