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

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

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

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

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

    /**
     * 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,注解如下:

    /**
     * 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参数)

@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)

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

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪力乌龟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值