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

相关推荐
l1t31 分钟前
DeepSeek总结的PostgreSQL使用 RDTSC 降低 EXPLAIN ANALYZE 的计时开销
数据库·postgresql
lagrahhn31 分钟前
Oracle中各个c版本介绍
数据库·oracle
却话巴山夜雨时i1 小时前
互联网大厂Java面试场景:Spring Boot、微服务与Redis实战解析
spring boot·redis·微服务·kafka·prometheus·java面试·电商场景
知识分享小能手1 小时前
MongoDB入门学习教程,从入门到精通,在生产环境中设置MongoDB(21)
数据库·学习·mongodb
XDHCOM1 小时前
ORA-12445报错:无法更改列隐藏属性,Oracle故障修复与远程处理,网友推荐解决方案
数据库·oracle
麒麟ZHAO1 小时前
鸿蒙flutter第三方库适配 - 文件对比工具
数据库·redis·flutter·华为·harmonyos
香蕉鼠片1 小时前
Redis
数据库·redis·缓存
翻斗包菜1 小时前
第 03 章 Python 操作 MySQL 数据库实战全解
数据库·python·mysql
SPC的存折1 小时前
1、MySQL故障排查与运维案例
linux·运维·服务器·数据库·mysql
小臭希1 小时前
Redis(NoSQL数据库,Linux-Ubuntu环境下)
数据库·redis·缓存