feat(cache/redisson): 新增 RedisLockUtils Redisson 分布式锁工具类

This commit is contained in:
书中自有颜如玉
2025-07-26 15:00:48 +00:00
committed by Charles7c
parent 49c804ac9e
commit 48783db422
2 changed files with 230 additions and 2 deletions

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.cache.redisson.util;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.continew.starter.core.util.SpringUtils;
import java.util.concurrent.TimeUnit;
/**
* Redisson分布式锁 工具类
*
* @author lishuyan
*/
public class RedisLockUtils implements AutoCloseable {
private static final Logger log = LoggerFactory.getLogger(RedisLockUtils.class);
/**
* 默认锁过期时间(毫秒)
*/
private static final long DEFAULT_EXPIRE_TIME = 10000L;
/**
* 默认获取锁超时时间(毫秒)
*/
private static final long DEFAULT_TIMEOUT = 5000L;
/**
* Redisson 客户端
*/
private static volatile RedissonClient CLIENT;
/**
* 锁实例
*/
private final RLock lock;
/**
* 是否成功获取锁
*/
private boolean isLocked;
/**
* 获取Redisson客户端实例
*
* @return RedissonClient实例
*/
private static RedissonClient getClient() {
if (CLIENT == null) {
synchronized (RedisLockUtils.class) {
if (CLIENT == null) {
CLIENT = SpringUtils.getBean(RedissonClient.class, false);
}
}
}
return CLIENT;
}
/**
* 私有构造函数,防止外部实例化
*/
private RedisLockUtils(RLock lock, long expireTime, long timeout, TimeUnit unit) {
this.lock = lock;
try {
this.isLocked = lock.tryLock(timeout, expireTime, unit);
if (isLocked) {
log.debug("获取锁成功key: {}", lock.getName());
} else {
log.debug("获取锁失败key: {}", lock.getName());
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error("获取锁过程中被中断key: {}", lock.getName(), e);
}
}
/**
* 尝试获取锁(启用看门狗自动续期机制)
*
* @param key 锁的键
* @param timeout 获取锁的超时时间
* @param unit 时间单位
* @return LockUtils 实例
*/
public static RedisLockUtils tryLockWithWatchdog(String key, long timeout, TimeUnit unit) {
RLock lock = getClient().getLock(key);
// 传入-1表示使用看门狗机制
return new RedisLockUtils(lock, -1, timeout, unit);
}
/**
* 尝试获取锁(启用看门狗自动续期机制,默认时间单位为毫秒)
*
* @param key 锁的键
* @param timeout 获取锁的超时时间(单位:毫秒)
* @return LockUtils 实例
*/
public static RedisLockUtils tryLockWithWatchdog(String key, long timeout) {
return tryLockWithWatchdog(key, timeout, TimeUnit.MILLISECONDS);
}
/**
* 尝试获取锁(启用看门狗自动续期机制,使用默认超时时间)
*
* @param key 锁的键
* @return LockUtils 实例
*/
public static RedisLockUtils tryLockWithWatchdog(String key) {
return tryLockWithWatchdog(key, DEFAULT_TIMEOUT);
}
/**
* 尝试获取锁
*
* @param key 锁的键
* @param expireTime 锁的过期时间
* @param timeout 获取锁的超时时间
* @param unit 时间单位
* @return LockUtils 实例
*/
public static RedisLockUtils tryLock(String key, long expireTime, long timeout, TimeUnit unit) {
RLock lock = getClient().getLock(key);
return new RedisLockUtils(lock, expireTime, timeout, unit);
}
/**
* 尝试获取锁(默认时间单位为毫秒)
*
* @param key 锁的键
* @param expireTime 锁的过期时间(单位:毫秒)
* @param timeout 获取锁的超时时间(单位:毫秒)
* @return LockUtils 实例
*/
public static RedisLockUtils tryLock(String key, long expireTime, long timeout) {
return tryLock(key, expireTime, timeout, TimeUnit.MILLISECONDS);
}
/**
* 尝试获取锁(使用默认过期时间和超时时间)
*
* @param key 锁的键
* @return LockUtils 实例
*/
public static RedisLockUtils tryLock(String key) {
return tryLock(key, DEFAULT_EXPIRE_TIME, DEFAULT_TIMEOUT);
}
/**
* 检查是否成功获取锁
*
* @return true成功false失败
*/
public boolean isLocked() {
return isLocked;
}
/**
* 释放锁
*/
@Override
public void close() {
if (isLocked && lock.isHeldByCurrentThread()) {
try {
lock.unlockAsync().get();
log.debug("释放锁成功key: {}", lock.getName());
} catch (Exception e) {
log.error("释放锁失败key: {}", lock.getName(), e);
}
} else {
log.debug("锁未被当前线程持有无需释放key: {}", lock.getName());
}
}
}

View File

@@ -28,6 +28,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
* Redis 工具类
@@ -51,6 +52,42 @@ public class RedisUtils {
return CLIENT;
}
/**
* 发布消息
*
* @param name 主题名称
* @param msg 发送数据
* @param consumer 自定义处理
*/
public static <T> void publish(String name, T msg, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(name);
topic.publish(msg);
consumer.accept(msg);
}
/**
* 发布消息
*
* @param name 主题名称
* @param msg 发送数据
*/
public static <T> void publish(String name, T msg) {
RTopic topic = CLIENT.getTopic(name);
topic.publish(msg);
}
/**
* 订阅消息
*
* @param name 主题名称
* @param clazz 消息类型
* @param consumer 自定义处理
*/
public static <T> void subscribe(String name, Class<T> clazz, Consumer<T> consumer) {
RTopic topic = CLIENT.getTopic(name);
topic.addListener(clazz, (channel, msg) -> consumer.accept(msg));
}
/**
* 设置缓存
*
@@ -104,7 +141,7 @@ public class RedisUtils {
/**
* 设置缓存
* <p>如果键不存在,则不设置</p>
*
*
* @param key 键
* @param value 值
* @return true设置成功false设置失败
@@ -117,7 +154,7 @@ public class RedisUtils {
/**
* 设置缓存
* <p>如果键不存在,则不设置</p>
*
*
* @param key 键
* @param value 值
* @param duration 过期时间