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

本文介绍了Redission中lock方法的阻塞特性,并推荐使用tryLock方法替代,以避免无限等待和资源浪费。通过增加waitTime参数,优化分布式锁的获取策略,提高代码的灵活性和效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如果想实现分布式锁请看上一篇文章【这样写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分布式锁才优雅】)

### Redission 分布式锁 使用 示例代码 以下是一个基于 Redission分布式锁的典型使用案例。通过 `RLock` 接口,Redission 提供了多种类型的锁功能,例如可重入锁、公平锁等[^1]。 #### 加锁与解锁示例 ```java import org.redisson.api.RLock; import org.redisson.api.RedissonClient; public class DistributedLockExample { private final RedissonClient redissonClient; public DistributedLockExample(RedissonClient redissonClient) { this.redissonClient = redissonClient; } public void performTaskWithLock(String lockKey) { RLock lock = redissonClient.getLock(lockKey); try { boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS); if (isLocked) { System.out.println("Lock acquired successfully."); // 执行业务逻辑 executeBusinessLogic(); } else { System.out.println("Failed to acquire the lock."); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("Thread was interrupted while waiting for the lock.", e); } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); // 解锁操作 System.out.println("Lock released."); } } } private void executeBusinessLogic() { // 模拟业务逻辑处理 System.out.println("Executing business logic..."); } } ``` 上述代码展示了如何利用 Redission 实现分布式锁的功能。主要步骤包括获取锁对象 (`RLock`)、尝试加锁 (`tryLock`) 和最终释放锁 (`unlock`)。如果未能成功获得锁,则会跳过业务逻辑部分。 #### 自动续期机制 Redission 支持锁的自动续期功能,在锁的有效期内如果没有完成任务,可以通过配置让锁自动延长有效时间,从而避免因超时而导致的锁丢失问题。此功能无需额外编码即可生效。 #### 测试多线程环境下的锁行为 为了验证分布式锁的实际效果,可以设计一个多线程测试场景。例如,模拟两个线程同时请求同一把锁的情况: ```java public static void main(String[] args) throws InterruptedException { Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redissonClient = Redisson.create(config); DistributedLockExample example = new DistributedLockExample(redissonClient); String sharedLockKey = "order_123_lock"; Runnable task = () -> example.performTaskWithLock(sharedLockKey); Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); thread1.join(); thread2.join(); redissonClient.shutdown(); } ``` 在此测试中,两个线程分别启动并试图获取名为 `"order_123_lock"` 的锁。由于分布式锁的存在,只有一个线程能够真正进入临界区执行业务逻辑,而另一个线程会被阻塞直到前一个线程释放锁[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

怪力乌龟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值