Redisson源码
锁重试
三个参数:
1、waitTime:获取锁的最大等待时间(没有传默认为-1)
2、leaseTime:锁自动释放的时间(没有传的话默认-1)
3、unit:时间的单位(等待时间和锁自动释放的时间单位)
假如没传leaseTime,则time就是指获取锁的最大等待时间(没有传默认为-1),而且leaseTime会给默认值
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
//将等待时间转化为毫秒
long time = unit.toMillis(waitTime);
//获取当前时间
long current = System.currentTimeMillis();
//获取线程ID
long threadId = Thread.currentThread().getId();
//尝试获取锁
//这里会有两种情况,一种是nil代表获取锁成功,一种是该key的剩余有效期代表回去锁失败
Long ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
if (ttl == null) {
//代表获取锁成功
return true;
} else {
//获取锁失败,要尝试再次获取
//使用当前时间减去之前记录当前时间,也就是获取锁消耗掉的时间
//再用最长等待时间,减去获取锁消耗的时间,得到的结果就是剩余等待时间
time -= System.currentTimeMillis() - current;
if (time <= 0L) {
//剩余等待时间小于0,获取锁失败
this.acquireFailed(waitTime, unit, threadId);
return false;
} else {
//剩余等待时间大于0,就可以继续尝试
//获取当前的时间,准备进行尝试
//但是不是立即尝试,因此刚才获取失败,如果立刻尝试,那个获取锁的线程大概率是没有完成业务释放锁的
current = System.currentTimeMillis();
//于是可以订阅别人是释放锁的信号——this.subscribe(threadId);
//可以这样,是因为在释放锁的时候会发布一条释放锁的通知
RFuture<RedissonLockEntry> subscribeFuture = this.subscribe(threadId);
//等待 这剩余最长等待时间 还没有释放锁的话,那么获取锁失败
if (!subscribeFuture.await(time, TimeUnit.MILLISECONDS)) {
//剩余最长等待时间内 其他线程还未释放锁 获取锁失败
if (!subscribeFuture.cancel(false)) {
subscribeFuture.onComplete((res, e) -> {
if (e == null) {
//会先取消订阅
this.unsubscribe(subscribeFuture, threadId);
}
});
}
this.acquireFailed(waitTime, unit, threadId);
//获取锁失败
return false;
} else {
boolean var16;
try {
//获取当前时间,当前时间减去之前记录的当前时间,得到的是获取锁成功花费的时间
//之前剩余的最长等待时间-获取锁成功花费的时间
time -= System.currentTimeMillis() - current;
if (time <= 0L) {
//剩余最长等待时间小于0,获取锁失败
this.acquireFailed(waitTime, unit, threadId);
boolean var20 = false;
return var20;
}
//这一次等待结束,最长等待时间依然有剩余
do {
//当前时间
long currentTime = System.currentTimeMillis();
//进行重试,尝试获取锁
//获取锁失败的返回值是该key的剩余过期时间
ttl = this.tryAcquire(waitTime, leaseTime, unit, threadId);
if (ttl == null) {
//获取锁成功
var16 = true;
return var16;
}
//计算获取锁花费的时间
//用剩余等待时间减去获取锁的时间
time -= System.currentTimeMillis() - currentTime;
if (time <= 0L) {
//剩余最长等待时间小于0,获取锁失败
this.acquireFailed(waitTime, unit, threadId);
var16 = false;
return var16;
}
/**
(重试机制)
下面这段代码设计得非常巧妙
它并不是无休止得循环等
而是先等待另外一个线程释放锁,再进程重试
这样设计减少了CPU的浪费
**/
//还剩下最长等待时间的大于0
currentTime = System.currentTimeMillis();
//ttl < time:也就是该key的剩余过期时间小于剩余的最长等待时间
if (ttl >= 0L && ttl < time) {
//使用信号量,因为释放锁的人会释放一个信号,这边就尝试获取信号
//如果是指定等待时间内,拿到了这个信号量返回true
//否则返回false
//那么只需要等待该key剩余等待时间即可
//消息ding
((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} else {
//如果ttl >= time
//那么只需要等待time,也就是剩余的最长等待时间
//如果这段时间都还没等到信号量,那么就证明失败了
//订阅
((RedissonLockEntry)subscribeFuture.getNow()).getLatch().tryAcquire(time, TimeUnit.MILLISECONDS);
}
//重置剩余的最长的等待时间
time -= System.currentTimeMillis() - currentTime;
} while(time > 0L);
//既然能从循环出来,证明time肯定小于等于0
//也就是最长等待时间都用完了,获取锁失败
this.acquireFailed(waitTime, unit, threadId);
var16 = false;
} finally {
//取消订阅
this.unsubscribe(subscribeFuture, threadId);
}
return var16;
}
}
}
}
坚冰还盖着北海的时候,我看到了怒放的梅花。

浙公网安备 33010602011771号