Redission获取锁
看下面一段代码,是Redisson获取锁最简单的实现方式
Java
// 从Redisson客户端获取RLock对象
RLock lock = redissonClient.getLock(key);
// 使用RLock对象中的tryLock方法获取锁
boolean tryLock = lock.tryLock();
// 判断是否回去锁成功,!true = fase ,!false = true
if (!tryLock){
return;
}
// 若获取锁成功则释放锁
lock.unlock();
补药小看这一段代码,Redisson提供了非常完善的锁实现的解决方案,让我们一点一点体会这段代码的美妙。
Redsson getLock()方法
我们点进getLock()方法,我们会看到这样一段代码
这段代码的意思就是new了一个RLock的实现类RedissonLock()返回了。其中name就是我们传入的key,也就是锁的key。那么connectionManager.getCommandExecutor()这是个什么东西呢?我们从字面意思可以得知这个是获取了一个命令执行器,那这个执行器是干啥的呢?
点进getCommandExecutor()方法
我们可以看到getCommandExecutor()方法是直接返回了CommandSyncService这个叫做命令同步服务的对象,那这个命令同步服务是干什么的呢? 点看进来看到CommandSyncService类继承了CommandAsyncService类,同时实现了CommandExecutor接口
看一下这里实现的方法,我们可以大概知道这个类是干什么的,CommandSyncService类是执行Redis同步命令的实现类。
Redisson tryLock()方法
我们点进去tryLock方法,我们可以发现tryLock方法有三个,分别是无参、两个参数和三个参数的。



tryLock()无参
我们先看无参的tryLock方法,首先进入tryLockAsync方法, 我们发现,tryLockAsync空参方法,其实就是调用了tryLockAsync有参方法,我们点进有参方法。
我们发现,在之前的tryLock方法中我们没有设置过期时间和等待时间,在这里都传入了-1,这里的-1的作用,我们后面再说。我们现在点进去tryAcquireOnceAsync方法看一下。
这治理我们可以看到,若是过期时间不为-1,则直接开始尝试获取锁,若过期时间为-1,则我们就设置默认的过期时间 30 * 1000 毫秒。在尝试锁成功之后调用scheduleExpirationRenewal方法安排锁的过期时间续期任务。 我们现在点进tryLockInnerAsync方法,看看Redisson是怎么获取锁的,这里我们选择RedissonLock的包。
我们看到这里其实就是使用lua脚本获取锁。
tryLock()三个参数
Java
@Override
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
long time = unit.toMillis(waitTime);
long current = System.currentTimeMillis();
long threadId = Thread.currentThread().getId();
Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
current = System.currentTimeMillis();
RFuture<RedissonLockEntry> subscribeFuture = subscribe(threadId);
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
unsubscribe(subscribeFuture, threadId);
}
});
}
acquireFailed(waitTime, unit, threadId);
return false;
}
try {
time -= System.currentTimeMillis() - current;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
while (true) {
long currentTime = System.currentTimeMillis();
ttl = tryAcquire(waitTime, leaseTime, unit, threadId);
// lock acquired
if (ttl == null) {
return true;
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
// waiting for message
currentTime = System.currentTimeMillis();
if (ttl >= 0 && ttl < time) {
subscribeFuture.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
subscribeFuture.getNow().getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
time -= System.currentTimeMillis() - currentTime;
if (time <= 0) {
acquireFailed(waitTime, unit, threadId);
return false;
}
}
} finally {
unsubscribe(subscribeFuture, threadId);
}
// return get(tryLockAsync(waitTime, leaseTime, unit));
}
这段代码大体上的执行流程是 这里我们注意一下,如果过期时间是-1的话,我们是会走watchDog续约机制的。什么是watchDog续约机制,我下一章专门讲一下。 我们现在把关键的方法说一下
tryAcquire()
点进去,我们发现和之前的空参一样的,只是这里的参数都是我们之前设置的不再是-1了。 我们进入tryAcquireAsync方法
我们会发现,由于我们走的是自己设置的过期时间,所以我们这次会走if中的代码,而不是下面的代码 上面和下面代码的差别就在于,leaseTime和waitTime。
tryLock()两个个参数
点进方法我们就可以看到,其实还是调用了tryLock方法,只是将过期时间设置为了-1而已。
Redission释放锁
我们点进unLock()方法,选择RedissonLock包下的unLock()方法 这里其实就只有一段核心代码,就是get(unlockAsync(Thread.currentThread().getId())); 这段代码是什么意思呢?我们先看 unlockAsync()方法。
这段代码其实就是异步解锁,然后判断解锁是否成功的 点进unlockInnerAsync方法,选择Redisson包下的
我们可以看到这其实就是一段lua代码去redis中尝试解锁。
这段代码就是对lua脚本返回的结果进行处理判断的逻辑,在解锁完成之后调用cancelExpirationRenewal取消续约任务,如果在解锁过程之中发生异常,就记录异常,如果解锁失败(当前线程未持有锁)就抛出异常,解锁成功就标记操作成功。

