RedissonLock源代码分析与锁应用

文章目录

  • 前言
  • 一、RedissonLock源代码分析
    • 1.1 尝试加锁
    • 2.2 解锁
  • 二、锁业务应用
    • 1.服务层方法@注解方式 注入锁
      • 1.1 定义DistributedLock @注解类
      • 1.2 定义DistributedLockAspect 切片类
      • 1.3 尝试获取锁代码片断
      • 1.4 释放锁代码片断
      • 1.5 服务层注入锁@注解
    • 2.代码行加锁
      • 2.1 pom.xml文件引入redisson、redis
      • 2.2 配置RedissonProperties属性类
      • 2.3 Redisson装配类
      • 2.4 Redisson实现类和工具类
      • 2.5 Redisson工具类RedissLockUtil
      • 2.6 方法体内使用锁


前言

通常服务端存在定时器业务时,如果分布式部署,多台同时执行会出现重复的数据,为了避免这种情况,可采用分布式锁。本章节讲解分布式锁RedissonLock以及锁的业务级的应用,内容包括两部分:

  1. RedissonLock源代码分析
  2. 锁业务应用
    • 接口方法@注解方式 注入锁
    • 代码行加锁

一、RedissonLock源代码分析

1.1 尝试加锁

主线:org.redisson.RedissonLock#tryLock(long waitTime, long leaseTime, TimeUnit unit)

public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {long time = unit.toMillis(waitTime);//取得最大等待时间long current = System.currentTimeMillis();//取得当前线程id(判断是否可重入锁的关键)long threadId = Thread.currentThread().getId();//1.尝试申请锁,返回还剩余的锁过期时间Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {return true;//2.ttl 为空,申请锁成功} else {//3.最大等待时间time <= 申请锁的耗时,则申请锁失败time = time - (System.currentTimeMillis() - current);if (time <= 0L) {this.acquireFailed(waitTime, unit, threadId);return false;} else {current = System.currentTimeMillis();/*** 4.订阅锁释放事件,并通过await方法阻塞等待锁释放:* 基于信息量,当锁被其它资源占用时,当前线程通过 Redis 的 channel 订阅锁的释放事件,* 一旦锁释放会发消息通知待等待的线程进行竞争*/RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);//await 方法内部是用CountDownLatch来实现阻塞,//获取subscribe异步执行的结果(应用Netty Future)if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {//await(...)返回false,等待时间已经超出获取锁最大等待时间,//取消订阅并返回获取锁失败if (!subscribeFuture.cancel(false)) {subscribeFuture.onComplete((res, e) -> {if (e == null) {this.unsubscribe(subscribeFuture, threadId);}});}this.acquireFailed(waitTime, unit, threadId);return false;} else {//await(...)返回true,进入循环尝试获取锁try {time -= System.currentTimeMillis() - current;if (time <= 0L) {this.acquireFailed(waitTime, unit, threadId);boolean var20 = false;return var20;} else {boolean var16;//5.收到锁释放的信号后,在最大等待时间之内,自旋尝试获取锁do {long currentTime = System.currentTimeMillis();// 再次尝试申请锁ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {var16 = true;return var16;//成功获取锁则直接返回true结束循环}time -= System.currentTimeMillis() - currentTime;if (time <= 0L) {this.acquireFailed(waitTime, unit, threadId);var16 = false;return var16;}currentTime = System.currentTimeMillis();//6.阻塞等待锁(通过信号量(共享锁)阻塞,等待解锁消息)if (ttl >= 0L && ttl < time) {// 在ttl时间内尝试获取信号量((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);} else {// 在time时间内尝试获取信号量((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);}time -= System.currentTimeMillis() - currentTime;} while(time > 0L);this.acquireFailed(waitTime, unit, threadId);var16 = false;return var16;}} finally {//7.无论是否获得锁,都要取消订阅解锁消息this.unsubscribe(subscribeFuture, threadId);}}}}}

订阅:RFuture subscribe(String entryName, String channelName)
‌发布订阅: 基于信号量的异步订阅机制,订阅指定的Redis频道channel,返回异步Future对象

abstract class PublishSubscribe<E extends PubSubEntry<E>> {public RFuture<E> subscribe(String entryName, String channelName) {AsyncSemaphore semaphore = this.service.getSemaphore(new ChannelName(channelName));RPromise<E> newPromise = new RedissonPromise();semaphore.acquire(() -> {if (!newPromise.setUncancellable()) {semaphore.release();} else {......//创建RedisPubSubListenerRedisPubSubListener<Object> listener = this.createListener(channelName,value);//订阅通道channelName、信号semaphore,绑定RedisPubSubListenerthis.service.subscribe(LongCodec.INSTANCE, channelName, semaphore, new RedisPubSubListener[]{listener});}});return newPromise;}......//创建监听器,用于监听指定频道的消息和订阅状态private RedisPubSubListener<Object> createListener(final String channelName, final E value) {RedisPubSubListener<Object> listener = new BaseRedisPubSubListener() {public void onMessage(CharSequence channel, Object message) {//接收到指定频道的消息时,开始处理消息if (channelName.equals(channel.toString())) {PublishSubscribe.this.onMessage(value, (Long)message);}}//当订阅成功时,标记 value 的 Promise 为成功。public boolean onStatus(PubSubType type, CharSequence channel) {if (!channelName.equals(channel.toString())) {return false;} else if (type == PubSubType.SUBSCRIBE) {value.getPromise().trySuccess(value);return true;} else {return false;}}};return listener;}
}

子线:tryAcquireAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId)

private <T> RFuture<Long> tryAcquireAsync(long waitTime, long leaseTime, 
TimeUnit unit, long threadId) {if (leaseTime != -1L) {return this.tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);} else {//tryLockInnerAsync-申请锁,申请锁并返回RFutureRFuture<Long> ttlRemainingFuture = this.tryLockInnerAsync(waitTime, this.internalLockLeaseTime, TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);ttlRemainingFuture.onComplete((ttlRemaining, e) -> {if (e == null) {if (ttlRemaining == null) {this.scheduleExpirationRenewal(threadId);}}});return ttlRemainingFuture;}}

tryLockInnerAsync-尝试异步加锁

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, 
RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);/*** 通过 EVAL 命令执行 Lua 脚本获取锁,保证了原子性*/return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,// 1.如果缓存中的key不存在,则执行 hset 命令(hset key UUID+threadId 1),然后通过 pexpire 命令设置锁的过期时间(即锁的租约时间)// 返回空值 nil ,表示获取锁成功"if (redis.call('exists', KEYS[1]) == 0) then " +"redis.call('hset', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +// 如果key已经存在,并且value也匹配,表示是当前线程持有的锁,则执行 hincrby 命令,重入次数加1,并且设置失效时间"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return nil; " +"end; " +//如果key已经存在,但是value不匹配,说明锁已经被其他线程持有,通过 pttl 命令获取锁的剩余存活时间并返回,至此获取锁失败"return redis.call('pttl', KEYS[1]);",//这三个参数分别对应KEYS[1],ARGV[1]和ARGV[2]Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
}
参数说明:KEYS[1]就是Collections.singletonList(getName()),表示分布式锁的keyARGV[1]就是internalLockLeaseTime,即锁的租约时间(持有锁的有效时间),默认30sARGV[2]就是getLockName(threadId),是获取锁时set的唯一值 value,即UUID+threadId

scheduleExpirationRenewal-redisson看门狗机制

  • 定期续租 Redisson 分布式锁的过期时间,防止锁因超时而被提前释放。
  • 创建一个定时任务,每隔internalLockLeaseTime / 3 毫秒执行一次;定时任务中调用 renewExpirationAsync(),异步续期锁,并递归继续下一次续期。
private void renewExpiration() {ExpirationEntry ee = (ExpirationEntry)EXPIRATION_RENEWAL_MAP.get(this.getEntryName());if (ee != null) {Timeout task = this.commandExecutor.getConnectionManager().newTimeout(new TimerTask() {public void run(Timeout timeout) throws Exception {ExpirationEntry ent = (ExpirationEntry)RedissonLock.EXPIRATION_RENEWAL_MAP.get(RedissonLock.this.getEntryName());if (ent != null) {Long threadId = ent.getFirstThreadId();if (threadId != null) {// 更新锁过期时间(lua脚本)RFuture<Boolean> future = RedissonLock.this.renewExpirationAsync(threadId);future.onComplete((res, e) -> {if (e != null) {RedissonLock.log.error("Can't update lock " + RedissonLock.this.getName() + " expiration", e);RedissonLock.EXPIRATION_RENEWAL_MAP.remove(RedissonLock.this.getEntryName());} else {if (res) {//递归调用 如果10秒后依然没有解锁,继续更新锁过期时间RedissonLock.this.renewExpiration();}}});}}}}, this.internalLockLeaseTime / 3L, TimeUnit.MILLISECONDS);ee.setTimeout(task);}}............/*异步续订分布式锁的过期时间,具体如下:使用 Lua 脚本保证操作的原子性;检查当前线程是否持有锁(通过 hexists 判断);如果持有,则通过 pexpire 延长锁的过期时间;返回值表示是否成功续订(1 成功,0 失败)*/protected RFuture<Boolean> renewExpirationAsync(long threadId) {return this.evalWriteAsync(this.getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +"redis.call('pexpire', KEYS[1], ARGV[1]); " +"return 1; " +"end; " +"return 0;", Collections.singletonList(this.getName()), this.internalLockLeaseTime, this.getLockName(threadId));}............
参数:
this.internalLockLeaseTime = commandExecutor.getConnectionManager()
.getCfg().getLockWatchdogTimeout();//默认30秒

2.2 解锁

org.redisson.RedissonLock#unlock()

protected RFuture<Boolean> unlockInnerAsync(long threadId) {/*** 通过 EVAL 命令执行 Lua 脚本获取锁,保证了原子性*/return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,//如果分布式锁存在,但是value不匹配,表示锁已经被其他线程占用,//当前客户端线程没有持有锁,不能主动解锁"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +"return nil;" +"end; " +//如果value匹配,将value减1,主要用在重入锁"local counter=redis.call('hincrby',KEYS[1], ARGV[3],-1);" +//重入次数减1后的值如果大于0,表示分布式锁有重入过,//那么只能更新失效时间,还不能删除"if (counter > 0) then " +"redis.call('pexpire', KEYS[1], ARGV[2]); " +"return 0; " +"else " +//重入次数减1后的值如果为0,这时就可以删除这个KEY,//并发布解锁消息,返回1"redis.call('del', KEYS[1]); " +"redis.call('publish', KEYS[2], ARGV[1]); " +"return 1; "+"end; " +"return nil;",Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
}

参数:
KEYS1:需要加锁的key,这里需要是字符串类型。
KEYS2:redis消息的ChannelName,一个分布式锁对应唯一的一个 channelName:“redisson_lock__channel__{” + getName() + “}”
ARGV1:redis消息体,这里只需要一个字节的标记就可以,主要标记redis的key已经解锁,再结合redis的Subscribe,能唤醒其他订阅解锁消息的客户端线程申请锁。
ARGV2:锁的超时时间,防止死锁
ARGV3 :锁的唯一标识,也就是刚才介绍的 id(UUID.randomUUID()) + “:” + threadId

删除过期信息

void cancelExpirationRenewal() {Timeout task = expirationRenewalMap.remove(getEntryName());if (task != null) {task.cancel();}
}

二、锁业务应用

1.服务层方法@注解方式 注入锁

1.1 定义DistributedLock @注解类

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {String key();int timeout() default 10; // 默认超时时间10秒int maxExecuteTime() default 30;TimeUnit timeUnit() default TimeUnit.SECONDS;
}

1.2 定义DistributedLockAspect 切片类

  拦截带有 @DistributedLock 注解的方法。
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;@Aspect
@Component
@Slf4j
public class DistributedLockAspect {@Resourceprivate RedissonClient redissonClient;private final ExpressionParser parser = new SpelExpressionParser();private final DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();@Around("@annotation(distributedLock)")public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {String methodName = joinPoint.getSignature().getName();String dynamicKey = parseSpEL(joinPoint, distributedLock.key());long timeout = distributedLock.timeout();long maxExecuteTime = distributedLock.maxExecuteTime();TimeUnit timeUnit = distributedLock.timeUnit();//解析keylog.info("[锁切面] 方法 {} 尝试获取锁,key={}, timeout={}{}", methodName, dynamicKey, timeout, timeUnit.name());RLock lock = redissonClient.getLock(dynamicKey);boolean isLocked = false;try {// 尝试获取锁isLocked = lock.tryLock(timeout, maxExecuteTime, timeUnit);if (!isLocked) {log.error("[Redisson锁] 获取锁失败,key={}", dynamicKey);throw new RuntimeException("系统繁忙,请重试");}log.info("[Redisson锁] 锁获取成功,key={}", dynamicKey);return joinPoint.proceed(); // 执行业务方法} finally {if (isLocked && lock.isHeldByCurrentThread()) {lock.unlock();log.info("[Redisson锁] 锁已释放,key={}", dynamicKey);}}}/*** 解析 SpEL 表达式*/private String parseSpEL(ProceedingJoinPoint joinPoint, String spEL) {if (!spEL.contains("#")) {return spEL;}Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();Object[] args = joinPoint.getArgs();// 创建 SpEL 上下文EvaluationContext context = new MethodBasedEvaluationContext(null, method, args, parameterNameDiscoverer);// 解析表达式Expression expression = parser.parseExpression(spEL);return expression.getValue(context, String.class);}
}

1.3 尝试获取锁代码片断

@Around("@annotation(distributedLock)")
public Object around(ProceedingJoinPoint joinPoint, 
DistributedLock distributedLock) {......String dynamicKey = parseSpEL(joinPoint, distributedLock.key());......RLock lock = redissonClient.getLock(dynamicKey);......// 尝试获取锁isLocked = lock.tryLock(timeout, maxExecuteTime, timeUnit);if (!isLocked) {log.error("[Redisson锁] 获取锁失败,key={}", dynamicKey);}else{log.info("[Redisson锁] 锁获取成功,key={}", dynamicKey);return joinPoint.proceed(); // 执行业务方法}
}............package org.redisson;
public class Redisson implements RedissonClient {public RLock getLock(String name) {return new RedissonLock(this.connectionManager.getCommandExecutor(), name);}
}

1.4 释放锁代码片断

 @Around("@annotation(distributedLock)")public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) { ............finally {if (isLocked && lock.isHeldByCurrentThread()) {lock.unlock();log.info("[Redisson锁] 锁已释放,key={}", dynamicKey);}}
}

1.5 服务层注入锁@注解

锁定方法、业务,同一时刻点  或 某一时段内 不可重复执行。
//订单job服务实现类OrderJobServiceImpl  
@Service
@RequiredArgsConstructor
@Slf4j
@RefreshScope
public class OrderJobServiceImpl  {/**** [描述]: 执行订单创建任务* 具备分布式锁防止并发执行,锁超时timeout为30秒* @date 2025/6/16 14:09* @param id* @param updateNextExecuteDate* @param executeAtDate*/@Override@DistributedLock(key = "#id", timeout = 30)public void execute(Long id) { ............//某一时刻点执行订单创建任务............
}

2.代码行加锁

2.1 pom.xml文件引入redisson、redis

   <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.14.1</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId></dependency>

2.2 配置RedissonProperties属性类

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
public class RedissonProperties {@Value("${spring.redis.host:}")private String redisHost;@Value("${spring.redis.port:}")private String redisPort;@Value("${spring.redis.sentinel.nodes:}")private String redisSentinelNodes;@Value("${spring.redis.cluster.nodes:}")private String redisClusterNodes;@Value("${spring.redis.sentinel.master:}")private String redisMasterName;@Value("${spring.redis.password:}")private String password;private String springRedis = "spring.redis";private int timeout = 10000;private int connectionPoolSize = 64;private int connectionMinimumIdleSize = 10;private int slaveConnectionPoolSize = 250;private int masterConnectionPoolSize = 250;public String[] getRedisSentinelNodes() {return redisSentinelNodes.contains(springRedis) ? null : redisSentinelNodes.split(",");}public String[] getRedisClusterNodes() {return redisClusterNodes.contains(springRedis) ? null : redisClusterNodes.split(",");}
}

2.3 Redisson装配类

  • 初始化RedissonClient
  • 装配锁Redisson实现类RedissonDistributedLockerServiceImpl,并将实例注入到RedissLockUtil中。
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;/*** @desc 装配类*/
@Configuration
@ConditionalOnClass(Config.class)
public class RedissonAutoConfiguration {@Resourceprivate RedissonProperties redissonProperties;private static final String REDIS_PROTOCOL_PREFIX = "redis://";/*** @version 哨兵模式自动装配*/private Config redissonMasterSentinel() {String[] nodes = redissonProperties.getRedisSentinelNodes();if (null == nodes || nodes.length == 0 || "".equals(nodes[0])) {return null;}List<String> clusterNodes = new ArrayList<>();for (String node : nodes) {clusterNodes.add(REDIS_PROTOCOL_PREFIX + node);}Config config = new Config();config.useSentinelServers().addSentinelAddress(clusterNodes.toArray(new String[0])).setMasterName(redissonProperties.getRedisMasterName()).setTimeout(redissonProperties.getTimeout()).setPassword(redissonProperties.getPassword()).setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize()).setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());return config;}/*** @version 集群模式自动装配*/private Config redissonClusterSentinel() {String[] nodes = redissonProperties.getRedisClusterNodes();if (null == nodes || nodes.length == 0 || "".equals(nodes[0])) {return null;}List<String> clusterNodes = new ArrayList<>();for (String node : nodes) {clusterNodes.add(REDIS_PROTOCOL_PREFIX + node);}Config config = new Config();config.useClusterServers().setScanInterval(1000).setTimeout(redissonProperties.getTimeout()).setPassword(redissonProperties.getPassword()).addNodeAddress(clusterNodes.toArray(new String[0])).setMasterConnectionPoolSize(redissonProperties.getMasterConnectionPoolSize()).setSlaveConnectionPoolSize(redissonProperties.getSlaveConnectionPoolSize());return config;}/*** 单机模式自动装配* @return*/private Config redissonAddressSingle() {Config config = new Config();if (null == redissonProperties.getRedisHost() || "".equals(redissonProperties.getRedisHost())) {return null;}config.useSingleServer().setAddress(REDIS_PROTOCOL_PREFIX+redissonProperties.getRedisHost() + ":" + redissonProperties.getRedisPort()).setTimeout(redissonProperties.getTimeout()).setPassword(redissonProperties.getPassword()).setConnectionPoolSize(redissonProperties.getConnectionPoolSize()).setConnectionMinimumIdleSize(redissonProperties.getConnectionMinimumIdleSize());return config;}/*** 初始化RedissonClient* 采用实例化顺序:集群模式 > 哨兵模式 > 单机模式*/@BeanRedissonClient redissonSingle() {Config config = redissonClusterSentinel();if (null == config) {config = redissonMasterSentinel();if (null == config) {config = redissonAddressSingle();}}if (null != config) {return Redisson.create(config);}return null;}/*** @version 装配RedissonDistributedLockerServiceImpl类,并将实例注入到RedissLockUtil*/@BeanDistributedLockerService distributedLocker(RedissonClient redissonClient) {RedissonDistributedLockerServiceImpl locker = new RedissonDistributedLockerServiceImpl();locker.setRedissonClient(redissonClient);RedissLockUtil.setLocker(locker);return locker;}
}

2.4 Redisson实现类和工具类

实现类RedissonDistributedLockerServiceImpl

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
/*** @version Redisson实现类*/@Service
public class RedissonDistributedLockerServiceImpl 
implements DistributedLockerService {private RedissonClient redissonClient;public void setRedissonClient(RedissonClient redissonClient) {this.redissonClient = redissonClient;}private static final String LOCK = "redisson_lock_";@Overridepublic RLock lock(String lockKey) {RLock lock = redissonClient.getLock(LOCK + lockKey);lock.lock();return lock;}@Overridepublic RLock lock(String lockKey, int leaseTime) {RLock lock = redissonClient.getLock(LOCK + lockKey);lock.lock(leaseTime, TimeUnit.SECONDS);return lock;}@Overridepublic RLock lock(String lockKey, TimeUnit unit, int timeout) {RLock lock = redissonClient.getLock(LOCK + lockKey);lock.lock(timeout, unit);return lock;}@Overridepublic boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {RLock lock = redissonClient.getLock(LOCK + lockKey);try {return lock.tryLock(waitTime, leaseTime, unit);} catch (InterruptedException e) {Thread.currentThread().interrupt();return false;}}@Overridepublic boolean isLocked(String lockKey) {RLock lock = redissonClient.getLock(LOCK + lockKey);return lock.isLocked();}@Overridepublic void unlock(String lockKey) {RLock lock = redissonClient.getLock(LOCK + lockKey);if (null != lock) {lock.unlock();}}@Overridepublic void unlock(RLock lock) {lock.unlock();}
}

2.5 Redisson工具类RedissLockUtil

import org.redisson.api.RLock;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;/*** @desc 工具类*/
@Component
public class RedissLockUtil {private static DistributedLockerService distributedLockerService;public static void setLocker(DistributedLockerService locker) {distributedLockerService = locker;}/*** 加锁* @param lockKey* @return*/public static RLock lock(String lockKey) {return distributedLockerService.lock(lockKey);}/*** 释放锁* @param lockKey*/public static void unlock(String lockKey) {distributedLockerService.unlock(lockKey);}/*** 释放锁* @param lock*/public static void unlock(RLock lock) {distributedLockerService.unlock(lock);}/*** 带超时的锁* @param lockKey* @param timeout 超时时间   单位:秒*/public static RLock lock(String lockKey, int timeout) {return distributedLockerService.lock(lockKey, timeout);}/*** 带超时的锁* @param lockKey* @param unit    时间单位* @param timeout 超时时间*/public static RLock lock(String lockKey, TimeUnit unit, int timeout) {return distributedLockerService.lock(lockKey, unit, timeout);}/*** 尝试获取锁* @param lockKey* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @return*/public static boolean tryLock(String lockKey, int waitTime, int leaseTime) {return distributedLockerService.tryLock(lockKey, TimeUnit.SECONDS, waitTime, leaseTime);}public static boolean isLocked(String lockKey) {return distributedLockerService.isLocked(lockKey);}/*** 尝试获取锁* @param lockKey* @param unit      时间单位* @param waitTime  最多等待时间* @param leaseTime 上锁后自动释放锁时间* @return*/public static boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int leaseTime) {return distributedLockerService.tryLock(lockKey, unit, waitTime, leaseTime);}}

2.6 方法体内使用锁

方法体内使用锁,锁定某段区间代码 间的业务。例如下述业务:挂起订单

   @Transactional(rollbackFor = Exception.class)public void hangUpOrder(String orderId) {Order order = orderMapper.findByOrderId(orderId);.......try {//加锁RedissLockUtil.lock(orderRole.getOrderUuid());Date time = new Date();if ("启用".equals(order.getOrderHangUpStatus())) {// 当前是非挂起状态,进行挂起操作............}} catch (Exception e) {log.error("挂起操作有误:{}", e.getMessage(), e);} finally {//释放锁RedissLockUtil.unlock(orderRole.getOrderUuid());}}

场景一:使用tryLock获取Redis锁。
调用tryLock方法来尝试获取锁。如果成功获得锁,则在持有锁时执行try块中的代码。如果无法获取锁,则执行else块中的代码。

RLock lock = redisson.getLock("myLock");
//lock.tryLock(10, TimeUnit.SECONDS);等待10秒钟,未指定leaseTime参数,
开启看门狗(watchdog)机制自动续约
//lock.tryLock(1L, 10L, TimeUnit.SECONDS);等待1秒钟,指定leaseTime参数10秒(锁从持有到自动释放的时长),watchdog机制不会自动续约
if (lock.tryLock()) {try {// Do some work while holding the lock} finally {lock.unlock();}
} else {// Failed to acquire the lock
}

场景二:使用lock获取Redis锁。
调用lock方法来获取锁。如果不能立即获得锁,代码将阻塞,直到锁可用为止。获取锁后,将执行try块中的代码,同时持有锁。最后,在Finally块中释放锁。

RLock lock = redisson.getLock("myLock");
long leaseTime = 10L;
//指定leaseTime参数10秒(锁从持有到自动释放的时长)
//lock.lock(leaseTime, TimeUnit.SECONDS);
lock.lock();
try {// Do some work while holding the lock
} finally {lock.unlock();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/87381.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/87381.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入理解mysql索引

一、什么是索引&#xff1f; 索引&#xff08;Index&#xff09; 是数据库管理系统中一种特殊的数据结构&#xff0c;存储在磁盘上。它包含对数据表中一列或多列的值进行排序&#xff0c;并存储了指向表中实际数据行物理位置或主键值的引用指针。可以把它类比为书籍的目录&…

VMware vSphere Foundation 9.0 技术手册 —— Ⅰ 安装 ESXi 9.0 (虚拟机)

目录 1. 安装 ESXi 9.0 (虚拟机)&#xff08;1&#xff09;ESXi Standard Boot Menu&#xff08;2&#xff09;ESXi 安装导向&#xff08;3&#xff09;最终用户许可协议&#xff08;4&#xff09;选择系统盘&#xff08;5&#xff09;选择键盘类型&#xff08;6&#xff09;设…

UE5 游戏模板 —— TopDownGame 俯视角游戏

UE5 游戏模板 —— TopDownGame 俯视角游戏 前言一、模块导入二、TopDownGameMode三、TopDownPlayerController1、构造函数2、SetupInputComponent初始化新输入系统处理输入逻辑 四、TopDownCharacter五、射线检测总结 前言 上一篇文章介绍了一下PuzzleGame模板的流程&#xf…

基于深度学习的智能图像分割系统:技术与实践

前言 图像分割是计算机视觉领域中的一个核心任务&#xff0c;其目标是将图像划分为多个有意义的区域或对象。图像分割在医学影像分析、自动驾驶、安防监控等多个领域有着广泛的应用。近年来&#xff0c;深度学习技术&#xff0c;尤其是卷积神经网络&#xff08;CNN&#xff09;…

【学习笔记】2.2 Encoder-Decoder

参考资料&#xff1a;https://github.com/datawhalechina/happy-llm 在 Transformer 中&#xff0c;使用注意力机制的是其两个核心组件——Encoder&#xff08;编码器&#xff09;和 Decoder&#xff08;解码器&#xff09;。 2.2.1 Seq2Seq 模型 Seq2Seq&#xff08;序列到…

# 材料力学押题

材料力学押题 文章目录 材料力学押题第一题第二题组合变形弯曲变形 第一题 Q 求力作用的销钉位置的竖直偏移距离。 S 方法一:能量方法 材料应变能计算为: U ∫ 内力 2 2 刚度 d A U\int \frac{\text{内力}^2}{2\times 刚度}\text{d}A U∫2刚度内力2​dA 克拉珀龙原理&…

uniapp项目之小兔鲜儿小程序商城(一) 项目介绍,技术栈,小程序的基础架构,封装拦截器和请求函数

文章目录 一.项目介绍和前置内容1.重要链接2.技术栈 二.创建uniapp项目1.使用HBuilderX创建2.使用命令行创建3.如何使用vscode开发uniapp项目?step1:把项目拉入vscode,开始下相关插件step2:ts类型校验step3:设置json文件可以允许注释 4.pages.json文件的作用是什么?5.示例:在…

Uniapp H5端SEO优化全攻略:提升搜索引擎排名与流量

在移动互联网时代&#xff0c;H5页面因其跨平台、低成本、易传播的特性&#xff0c;成为许多企业的首选。Uniapp作为一款优秀的前端跨端开发框架&#xff0c;能够快速开发H5、小程序、App等多端应用。然而&#xff0c;由于Uniapp默认采用SPA&#xff08;单页应用&#xff09;架…

一[3.3]、ubuntu18.04环境 利用 yolov8 实现列车轨道检测,并提取正确的轨道线【全网最详细】

一、参考文献 https://blog.csdn.net/u010117029/category_12977729.html 一[3.2]、ubuntu18.04环境 利用 yolov8 训练开源列车数据集,并实现列车轨道检测【全网最详细】-CSDN博客 火车轨道铁路轨道检测识别(附带Python源码+详细解析)_轨道点云提取铁轨代码-CSDN博客

pp-ocrv5中的改进-跨阶段特征融合(CSP-PAN)以及在 Neck 部分引入 CSP-PAN后为何就能解决小文本漏检问题?

好的&#xff0c;我们来详细解析一下 PP-OCRv5 中的**跨阶段特征融合&#xff08;CSP-PAN&#xff09;**改进&#xff0c;以及它如何有效解决小文本漏检问题。 背景&#xff1a;PP-OCR 的 Neck 部分与 PAN 在 PP-OCRv3 及之前的版本中&#xff0c;Neck 部分使用的是标准的 **…

【数据分析四:Data Preprocessing】数据预处理

一、数据预处理 直接收集的数据通常是“脏的”&#xff1a; 不完整、不准确、不一致 数据预处理是进行大数据的分析和挖掘的工作中占工作量最大的一个步骤&#xff08;80%&#xff09; 二、处理缺失值 处理缺失数据的方法&#xff1a;首先确认缺失数据的影响 数据删除&#x…

一起来入门深度学习知识体系

前言 什么是深度学习&#xff1f;它有什么魔力&#xff1f; 想象一个机器人能识别人脸、写诗、翻译语言、甚至和你聊天。它是怎么学会这些能力的&#xff1f;答案正是——深度学习&#xff08;Deep Learning&#xff09;。 简单来说&#xff0c;深度学习就像是教会一台计算机…

Prompt+Agent+LLM:半导体炉管设备健康评估的落地实战

引言 在高端制造业的核心场景中&#xff0c;设备健康管理正面临前所未有的挑战。以半导体制造为例&#xff0c;一台价值数百万美元的炉管设备意外停机&#xff0c;可能导致整条产线瘫痪、晶圆批次报废&#xff0c;单日损失可达千万级。传统基于阈值规则的监控系统难以捕捉早期…

PostgreSQL的扩展bloom

PostgreSQL的扩展bloom 一、扩展概述 bloom 是 PostgreSQL 提供的一个基于**布隆过滤器(Bloom Filter)**的索引扩展&#xff0c;特别适合多列任意组合查询的优化场景。 二、核心特性 特性描述优势多列索引单索引支持多列组合减少索引数量模糊匹配高效处理和IN查询优于B-tre…

算法与数据结构学习之旅:从入门到进阶

在计算机科学的浩瀚宇宙中&#xff0c;算法与数据结构如同闪耀的恒星&#xff0c;驱动着整个程序世界的运转。无论是手机上流畅运行的 APP&#xff0c;还是搜索引擎瞬间返回的海量结果&#xff0c;背后都离不开算法与数据结构的精妙设计。对于想要深入探索计算机领域的开发者和…

C++map和set类(简介)

文章目录 一、关联式容器二、键值对三、树形结构的关联式容器3.1 set类的简介3.2 set的接口3.2.1 set的模版参数列表3.2.2 set的构造3.2.3 set的迭代器3.2.4 set的容量3.2.5 set的修改操作 3.3 set的使用案例3.4 multiset类的介绍3.5 multiset的使用案例3.6 map类的简介3.7 map…

圆柱电池自动化升级:面垫机如何破解生产痛点?

在圆柱电池的生产流程中&#xff0c;面垫&#xff08;绝缘垫片&#xff09;的安装是保障电池安全与性能的关键环节。传统手工操作不仅效率低&#xff0c;还容易出现面垫偏移、漏贴等问题&#xff0c;影响产品一致性。圆柱电池自动面垫机的出现&#xff0c;通过自动化技术解决了…

【AI Study】第四天,Pandas(1)- 基础知识

文章概要 本文详细介绍 Pandas 库的基础知识&#xff0c;包括&#xff1a; Pandas 的基本概念和特点安装和配置方法核心数据结构&#xff08;Series 和 DataFrame&#xff09;各种数据类型的处理方法实际应用示例 什么是 Pandas Pandas 是 Python 中最流行的数据分析库之一…

重构气血经络的数学模型:气血经络级联控制系统核心方程

从融智学视域&#xff0c;重构气血经络的数学模型 摘要&#xff1a; 融智学视域&#xff0c;通过三元耦合框架&#xff0c;重构气血经络模型&#xff0c;建立跨学科认知体系。五大分支协同运作&#xff1a;数学融智学构建纤维丛模型&#xff0c;逻辑融智学建立防歧义语义网&…

python爬虫:某网站价格数字加密破解

文章目录 前言一、案例二、破解流程1.原理2.找到woff文件3.分析woff文件4.代码实现1.转化woff文件2.绘图并ocr识别3.映射数据 三、总结 前言 有时我们在进行网页抓取采集数据时&#xff0c;有些重要的数据比如说价格,数量等信息会进行加密&#xff0c;通过复制或者简单的采集是…