SpringCloud 分布式锁Redisson锁的重入性与看门狗机制 高并发 可重入
可重入
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(); //+1System.out.println("Lock acquired for the first time!");// 业务逻辑:扣款deductBalance(lock);} finally {// 释放锁:必须要释放与 lock.lock() 相同次数的 unlock() 才能完全释放锁lock.unlock(); //-1System.out.println("Lock released after first operation.");}}// 模拟业务逻辑:扣款操作private static void deductBalance(RLock lock) {// 业务逻辑需要在同一个线程中再次获取锁(模拟可重入性)lock.lock();//+1try {System.out.println("Lock acquired for the second time, performing deduct balance operation...");// 扣款逻辑,比如账户余额减少System.out.println("Balance deducted!");} finally {// 释放锁lock.unlock();//-1System.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();// 尝试获取锁,并获得锁的剩余有效时间 ttlLong 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) {// 每次进入循环时,重新尝试获取锁的剩余时间 ttlttl = 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) {// 如果需要响应中断,抛出 InterruptedExceptionthrow 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 方法,锁的超时时间会在持有锁的线程未释放锁的情况下自动延长,避免锁超时。
- 避免死锁:如果线程因为异常等原因没有释放锁,看门狗机制会确保锁最终被释放。