Redisson获取锁主要分为3步:
1、tryAcquire尝试获取锁
2、第一次没有获取到锁,订阅锁释放的消息
3、循环尝试获取锁,直到成功或者超时
一、tryAcquire尝试获取锁
首先通过tryAcquire方法尝试获取锁,获取成功则返回的ttl为null,否则返回已经被持有锁的ttl
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;
}
tryAcquire方法的核心是通过一段lua脚本,去redis中创建一个hash,该hash的key是锁的key,hash中的一个key是一段字符串加线程id,对应的value是进入次数。lua脚本如下:
if (redis.call('exists', KEYS[1]) == 0)
then redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
then redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
return nil;
end;
return redis.call('pttl', KEYS[1]);
KEYS[1]是锁的key
ARGV[1]是锁的持有时间
ARGV[2]是持有锁的ID+线程ID
1、首先判断这把锁是否存,不存在的话,直接获取到锁
2、如果锁已经存在,判断是否是当前线程获取到的锁,如果是,则给锁的进入次数加一
3、如果锁已经存在,但是是别的线程获取到的锁,则返回锁的ttl
二、第一次没有获取到锁,订阅锁释放的消息
通过设定await的超时时间,如果超时了还没有监听到锁释放的消息,就认为获取锁超时,代码如下:
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;
}
三、循环尝试获取锁,直到成功或者超时
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;
}
}
1、尝试获取锁,获取到了退出,返回获取成功
2、没有获取到锁,计算是否有剩余时间,没有时间了就退出,返回获取失败
3、还有时间,则比较剩余时间和已经存在锁的ttl,取较小值,继续等待锁释放
4、锁释放后,计算是否有剩余时间,还有就回到第1步,没有就退出,返回获取失败
版权声明:本文为zjg_tiancai原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。