【redission分布式锁】到底要用lock还是tryLock

如果想实现分布式锁请看上一篇文章【这样写redission分布式锁才优雅

上一篇文章留了一个小尾巴,redission的lock方法是会阻塞的,具有一定的风险,

  1. 如果获取锁的线程一直不释放锁或者占用锁时间过长,那么其他线程只能一直等待,造成资源浪费甚至死锁
  2. 如果有心之人发现你的方法存在阻塞,有可能利用这个进行DOS攻击,造成服务器瘫痪

我们翻开lock方法的源码,有这么一个注释:

java 复制代码
    /**
     * Acquires the lock with defined <code>leaseTime</code>.
     * Waits if necessary until lock became available.
     *
     * Lock will be released automatically after defined <code>leaseTime</code> interval.
     *
     * @param leaseTime the maximum time to hold the lock after it's acquisition,
     *        if it hasn't already been released by invoking <code>unlock</code>.
     *        If leaseTime is -1, hold the lock until explicitly unlocked.
     * @param unit the time unit
     *
     */
    void lock(long leaseTime, TimeUnit unit);

意思就是说,这是一个获取锁的方法,但是如果锁已经存在会进行等待。这个等待是没有时间限制的,有兴趣可以看一下源码,利用的是while(true)循环。

总觉得不够优雅,还好redission还提供了一个方法叫tryLock,注解如下:

java 复制代码
    /**
     * Tries to acquire the lock with defined <code>leaseTime</code>.
     * Waits up to defined <code>waitTime</code> if necessary until the lock became available.
     *
     * Lock will be released automatically after defined <code>leaseTime</code> interval.
     *
     * @param waitTime the maximum time to acquire the lock
     * @param leaseTime lease time
     * @param unit time unit
     * @return <code>true</code> if lock is successfully acquired,
     *          otherwise <code>false</code> if lock is already set.
     * @throws InterruptedException - if the thread is interrupted
     */
    boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException;

注解意思是,获取锁,获取不到就等待,但是waitTime耗尽的时候就不再等待,并返回false。跟lock的区别有两个,一是不会无限等待,甚至可以不等待(waitTime设置为0);二是有返回值,代表是否占用锁成功。

稍加对比,就会发现redission的lock方法就像是synchronized,沉重又不优雅,而tryLock保留lock功能的同时拒绝了无限等待。这两个方法该怎么选用一目了然。

我们回想一下上一篇文章的案例,订单支付成功时,调用发货方法。同一笔订单只能发货一次,所以我们根本没必要等锁,当发现相同订单号已经占用锁的时候,我们完全可以第一时间退出,节约资源。其实大多数使用分布式锁的场景都是如此,所以我们对上文中的注解进行改造如下:

改造注解(增加waitTime参数)

c 复制代码
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RedisLock {

	/**
	 * redis锁 名字
	 */
	String lockName() default "";

	/**
	 * redis锁 key 支持spel表达式
	 */
	String key() default "";

	/**
	 * 过期秒数,默认为5毫秒
	 *
	 * @return 轮询锁的时间
	 */
	int expire() default 5000;

	/**
	 * 等锁时间,默认0秒
	 * @return 等锁时间
	 */
	int waitTime() default 0;

	/**
	 * 超时时间单位
	 *
	 * @return 秒
	 */
	TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

改造切面实现(lock改为tryLock)

c 复制代码
	@Around("@annotation(redisLock)")
	public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
		String spel = redisLock.key();
		String lockName = redisLock.lockName();

		RLock rLock = redissonClient.getLock(getRedisKey(joinPoint,lockName,spel));

		boolean isLock = false;
		Object result = null;
		try{
			isLock = rLock.tryLock(redisLock.waitTime(),redisLock.expire(),redisLock.timeUnit());
			if (isLock) {
				//执行方法
				result = joinPoint.proceed();
			} else {
				log.info("获取锁{}失败", getRedisKey(joinPoint, lockName, spel));
			}
		} finally {
			rLock.unlock();
		}
		return result;
	}

注解的使用不做改动,waitTime不赋值就会使用默认的0秒,也就是获取不到锁立刻退出,节约资源。(见【这样写redission分布式锁才优雅】)

相关推荐
咖啡啡不加糖2 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
MickeyCV2 小时前
使用Docker部署MySQL&Redis容器与常见命令
redis·mysql·docker·容器·wsl·镜像
棠十一3 小时前
Rabbitmq
分布式·docker·rabbitmq
肥仔哥哥19303 小时前
springCloud2025+springBoot3.5.0+Nacos集成redis从nacos拉配置起服务
redis·缓存·最新boot3集成
Lansonli3 小时前
大数据Spark(六十一):Spark基于Standalone提交任务流程
大数据·分布式·spark
Theodore_10225 小时前
大数据(2) 大数据处理架构Hadoop
大数据·服务器·hadoop·分布式·ubuntu·架构
呼拉拉呼拉5 小时前
Redis故障转移
数据库·redis·缓存·高可用架构
什么都想学的阿超5 小时前
【Redis系列 04】Redis高可用架构实战:主从复制与哨兵模式从零到生产
数据库·redis·架构
我命由我123458 小时前
Spring Boot 项目集成 Redis 问题:RedisTemplate 多余空格问题
java·开发语言·spring boot·redis·后端·java-ee·intellij-idea
篱笆院的狗9 小时前
如何使用 Redis 快速实现布隆过滤器?
数据库·redis·缓存