1. 测试代码,以黑马点评项目P67视频为基础


import lombok.extern.slf4j.Slf4j;  
import org.junit.jupiter.api.BeforeEach;  
import org.junit.jupiter.api.Test;  
import org.redisson.api.RLock;  
import org.redisson.api.RedissonClient;  
import org.springframework.boot.test.context.SpringBootTest;  
  
import javax.annotation.Resource;  
import java.util.concurrent.TimeUnit;  
@Slf4j  
@SpringBootTest  
class RedissonTest {  
    @Resource  
    private RedissonClient redissonClient;  
    private RLock lock;  
    @BeforeEach  
    void setUp() {  
        lock = redissonClient.getLock("order");  
    }  
    @Test  
    void method1() throws InterruptedException {  
        // 尝试获取锁  
        boolean isLock = lock.tryLock(1L, TimeUnit.SECONDS); //锁的过期时间默认为30s
        if (!isLock) {  
            log.error("获取锁失败 .... 1");  
            return;  
        }  
        try {  
            log.info("获取锁成功 .... 1");  
            method2();  
            log.info("开始执行业务 ... 1");  
        } finally {  
            log.warn("准备释放锁 .... 1");  
            lock.unlock();  
        }  
    }  
    void method2() {  
        // 尝试获取锁  
        boolean isLock = lock.tryLock();  
        if (!isLock) {  
            log.error("获取锁失败 .... 2");  
            return;  
        }  
        try {  
            log.info("获取锁成功 .... 2");  
            log.info("开始执行业务 ... 2");  
        } finally {  
            log.warn("准备释放锁 .... 2");  
            lock.unlock();  
        }  
    }  
}

2. 锁重试运行逻辑分析


点击查看lock.tryLock(1L, TimeUnit.SECONDS);的源码实现

redis看门狗修改relaseTime redisson看门狗开启_java

在Long ttl=tryAcquire(waitTime,leaseTime,unit,threadId);尝试获取锁,开始进行获取锁的业务逻辑,点击进入tryAcquire函数

redis看门狗修改relaseTime redisson看门狗开启_redis_02

在tryAcquire函数中调用了tryAcquireAsync函数,下面为tryAcquireAsync函数

redis看门狗修改relaseTime redisson看门狗开启_java_03

随后执行TryLockInnerAsync函数,通过执行lua脚本来保证原子性获取锁

redis看门狗修改relaseTime redisson看门狗开启_java_04

lua脚本解释:

  1. 判断锁是否存在,若不存在则设置锁,并将value设为1,设置过期时间
  2. 如果锁存在,判断是否是自己的锁,若是,则将value+1,重置过期时间
  3. 若不是自己的锁,则锁的剩余过期时间

该函数返回null(获取锁成功)或者锁的剩余过期时间,在tryLock函数中会进行判断,如果为null,则返回true

否则,则获取当前时间,订阅锁过期通知,订阅等待时间是有限的(值为剩余等待时间)

redis看门狗修改relaseTime redisson看门狗开启_等待时间_05

当在规定时间等到了锁过期通知,则会执行以下部分,仍会进行锁等待时间的判断

redis看门狗修改relaseTime redisson看门狗开启_java_06

之后便进行获取锁的重试

redis看门狗修改relaseTime redisson看门狗开启_redis_07

业务流程图分析如下:

redis看门狗修改relaseTime redisson看门狗开启_redis_08

3. 锁超时运行逻辑分析


当线程1业务因为阻塞,导致锁超时释放,而线程2拿到了该锁,当线程1业务执行完毕,释放锁,会导致线程2的锁丢失,其他线程可以去获取锁

在执行ttlRemainingFuture.onComplete时,ttlRemaining表示剩余有效期,e表示异常

redis看门狗修改relaseTime redisson看门狗开启_c函数_09

当剩余有效期为null的时候,执行scheduleExpirationRenewal函数释放锁,下面为scheduleExpirationRenewal函数

redis看门狗修改relaseTime redisson看门狗开启_java_10

该函数会根据线程Id,进行创建锁,如果不是第一次进入,则更新有效期。在renewExpiration()函数中执行更新有效期,最终会执行lua脚本

redis看门狗修改relaseTime redisson看门狗开启_redis_11

全流程的流程图如下图所示:

redis看门狗修改relaseTime redisson看门狗开启_redis_12