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 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/zjg_tiancai/article/details/128413422