【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分布式锁才优雅】)

相关推荐
G_whang28 分钟前
centos7下docker 容器实现redis主从同步
redis·docker·容器
.生产的驴41 分钟前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
隔着天花板看星星1 小时前
Spark-Streaming集成Kafka
大数据·分布式·中间件·spark·kafka
我叫啥都行4 小时前
计算机基础复习12.22
java·jvm·redis·后端·mysql
阿乾之铭5 小时前
Redis四种模式在Spring Boot框架下的配置
redis
技术路上的苦行僧5 小时前
分布式专题(8)之MongoDB存储原理&多文档事务详解
数据库·分布式·mongodb
龙哥·三年风水5 小时前
workman服务端开发模式-应用开发-后端api推送修改二
分布式·gateway·php
小小工匠6 小时前
分布式协同 - 分布式事务_2PC & 3PC解决方案
分布式·分布式事务·2pc·3pc
on the way 1236 小时前
Redisson锁简单使用
redis
科马7 小时前
【Redis】缓存
数据库·redis·spring·缓存