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

相关推荐
JIngJaneIL7 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
微学AI8 小时前
复杂时序场景的突围:金仓数据库是凭借什么超越InfluxDB?
数据库
廋到被风吹走8 小时前
【数据库】【Redis】定位、优势、场景与持久化机制解析
数据库·redis·缓存
有想法的py工程师9 小时前
PostgreSQL + Debezium CDC 踩坑总结
数据库·postgresql
Nandeska9 小时前
2、数据库的索引与底层数据结构
数据结构·数据库
小卒过河010410 小时前
使用apache nifi 从数据库文件表路径拉取远程文件至远程服务器目的地址
运维·服务器·数据库
过期动态10 小时前
JDBC高级篇:优化、封装与事务全流程指南
android·java·开发语言·数据库·python·mysql
Mr.朱鹏10 小时前
SQL深度分页问题案例实战
java·数据库·spring boot·sql·spring·spring cloud·kafka
一位代码10 小时前
mysql | 常见日期函数使用及格式转换方法
数据库·mysql
SelectDB10 小时前
Apache Doris 4.0.2 版本正式发布
数据库·人工智能