SpringCloud 分布式锁Redisson锁的重入性与看门狗机制 高并发 可重入

发布于:2025-06-05 ⋅ 阅读:(30) ⋅ 点赞:(0)

可重入

Redisson 的锁支持 可重入性,这意味着同一个线程在获取锁后,如果再次尝试获取该锁,它可以成功地获得锁,而不会被阻塞。

  • 每次一个线程成功获取锁后,它的持有次数会增加。当线程再次获取该锁时,Redisson 会检查该线程是否已经持有锁。如果是,它会允许该线程再次获取锁,并将持有次数递增。
  • 每次释放锁时,持有次数会递减,直到持有次数变为零,锁才会被完全释放。

在这里插入图片描述

  public static void main(String[] args) {
        // 创建 Redisson 客户端配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://localhost:6379"); // 连接到本地 Redis 服务器
        RedissonClient redisson = Redisson.create(config);

        // 获取分布式锁
        RLock lock = redisson.getLock("accountLock");

        try {
            // 模拟账户操作的过程:先获取锁,进行第一次操作
            lock.lock();  //+1
            System.out.println("Lock acquired for the first time!");

            // 业务逻辑:扣款
            deductBalance(lock);

        } finally {
            // 释放锁:必须要释放与 lock.lock() 相同次数的 unlock() 才能完全释放锁
            lock.unlock();  //-1
            System.out.println("Lock released after first operation.");
        }
        
    }

    // 模拟业务逻辑:扣款操作
    private static void deductBalance(RLock lock) {
        // 业务逻辑需要在同一个线程中再次获取锁(模拟可重入性)
        lock.lock();//+1
        try {
            System.out.println("Lock acquired for the second time, performing deduct balance operation...");
            // 扣款逻辑,比如账户余额减少
            System.out.println("Balance deducted!");
        } finally {
            // 释放锁
            lock.unlock();//-1
            System.out.println("Lock released after deduct balance operation.");
        }
    }

当value值为0时就会释放锁。
在这里插入图片描述
这就是锁的可重入性。


看门狗机制

看门狗机制用于确保分布式系统中,锁或资源的持有者在预定时间内释放锁,否则看门狗会自动释放该锁,避免死锁等问题。帮助避免因某些异常(如程序崩溃或线程挂起)而导致锁被永久占用。

  • 自动续期:定期地自动刷新锁的过期时间。当锁被一个线程或进程持有时,会定期更新锁的 TTL(过期时间),以防止它被过期。即使持锁者在持有锁的过程中没有显式地释放锁,会确保锁不会在持有者不主动释放时过期,从而避免因过期造成的错误。

  • 自动解锁:如果持锁者超时或出现故障,会在检测到持锁者没有继续更新锁的超时时间后,自动释放锁,避免死锁。

  • 配置超时时间:可以与锁的过期时间和续期机制结合使用,可以通过配置来指定超时时间和自动续期的时间间隔。

在这里插入图片描述

相关源代码
尝试获取锁,如果不能获取锁,一直等待直到获取成功或者达到超时限制。中间部分涉及到锁的租期和超时处理,保证锁的可用性和避免死锁。interruptibly 参数提供了是否支持中断等待的选项。

private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
    // 获取当前线程的ID,用于标识是哪一个线程请求锁
    long threadId = Thread.currentThread().getId();

    // 尝试获取锁,并获得锁的剩余有效时间 ttl
    Long ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);

    // 如果成功获得锁(ttl != null),则进入锁处理逻辑
    if (ttl != null) {
        // 订阅当前线程的锁操作,返回一个 RFuture 对象,用于后续的异步操作
        RFuture<RedissonLockEntry> future = this.subscribe(threadId);

        // 根据 interruptibly 参数决定同步执行订阅操作
        if (interruptibly) {
            // 如果需要响应中断,使用带中断支持的同步方法
            this.commandExecutor.syncSubscriptionInterrupted(future);
        } else {
            // 如果不需要响应中断,直接使用同步方法
            this.commandExecutor.syncSubscription(future);
        }

        try {
            // 启动一个无限循环来持续尝试获取锁
            while (true) {
                // 每次进入循环时,重新尝试获取锁的剩余时间 ttl
                ttl = this.tryAcquire(-1L, leaseTime, unit, threadId);

                // 如果 ttl 为 null,表示锁已经过期或无法获取,跳出循环
                if (ttl == null) {
                    return;
                }

                // 如果 ttl >= 0L,表示可以继续持有锁
                if (ttl >= 0L) {
                    try {
                        // 尝试在 ttl 指定的时间内获取 latch
                        ((RedissonLockEntry) future.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    } catch (InterruptedException var13) {
                        // 如果在获取 latch 时发生 InterruptedException,则根据 interruptibly 变量决定是否抛出异常
                        if (interruptibly) {
                            // 如果需要响应中断,抛出 InterruptedException
                            throw var13;
                        }

                        // 如果不需要响应中断,尝试继续获取 latch
                        ((RedissonLockEntry) future.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
                    }
                } else if (interruptibly) {
                    // 如果 ttl 为负值并且需要响应中断,调用 acquire 方法阻塞直到获取锁
                    ((RedissonLockEntry) future.getNow()).getLatch().acquire();
                } else {
                    // 如果 ttl 为负值并且不需要响应中断,调用 acquireUninterruptibly 方法阻塞直到获取锁
                    ((RedissonLockEntry) future.getNow()).getLatch().acquireUninterruptibly();
                }
            }
        } finally {
            // 最终,取消订阅,释放资源
            this.unsubscribe(future, threadId);
        }
    }
}

  • 自动续期:通过 ttl 值和 latch.tryAcquire 方法,锁的超时时间会在持有锁的线程未释放锁的情况下自动延长,避免锁超时。
  • 避免死锁:如果线程因为异常等原因没有释放锁,看门狗机制会确保锁最终被释放。

网站公告

今日签到

点亮在社区的每一天
去签到