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

相关推荐
ltl5 小时前
向量检索引擎选型:决策树、RAG 回链与开放问题
数据库
坚持学习前端日记6 小时前
Python SQLAlchemy ORM 从0到1精通实战手册(基础到复杂高阶)
数据库·python·oracle
灯澜忆梦6 小时前
【MySQL17】进阶篇 | InnoDB引擎
数据库·mysql
anxiao_m6 小时前
2026制造业云桌面选型攻略,不同生产场景适配方案汇总
大数据·网络·数据库
许彰午6 小时前
# 数据库配拦截器,不用改BPMN
数据库
lv__pf7 小时前
redis【msb 2026金三银四redis上】
数据库·redis·缓存
布莱克6058 小时前
理解数据库聚簇索引:原理、优势与适用场景
数据库·mysql
Nturmoils8 小时前
只面对一张表:KingbaseES 超表如何简化海量时序数据管理
数据库
starrocks_stella9 小时前
StarRocks 如何查询 Paimon 半结构化数据?Variant、Shredding 与 SQL 实践
数据库