redission 分布锁
redission 分布锁使用了map 这种数据结构,key为锁的名称,map的key客户端,value为重入的个数
代码进行分析,大致代码都写到RedissionLock这个类里。
加锁:
ivate void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException {
long threadId = Thread.currentThread().getId();
//先进行获取锁
Long ttl = this.tryAcquire(leaseTime, unit, threadId);
//如果获取锁失败
if (ttl != null) {
//进行订阅,注意这是一个同步操作,如果channel 里没有消息,会一直阻塞,当其他线程进行锁的释放,会进行消息的发送
//如果正常操作的话,会进行轮询,但这样对redis 压力太大,所以使用发布订阅来实现
RFuture<RedissonLockEntry> future = this.subscribe(threadId);
this.commandExecutor.syncSubscription(future);
try {
while(true) {
ttl = this.tryAcquire(leaseTime, unit, threadId);
if (ttl == null) {
return;
}
if (ttl >= 0L) {
try {
this.getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
} catch (InterruptedException var13) {
if (interruptibly) {
throw var13;
}
this.getEntry(threadId).getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
}
} else if (interruptibly) {
this.getEntry(threadId).getLatch().acquire();
} else {
this.getEntry(threadId).getLatch().acquireUninterruptibly();
}
}
} finally {
this.unsubscribe(future, threadId);
}
}
}
tryAcquire 里执行操作就是如下的代码:
//判断该锁是否存在,如果该锁不存在
if (redis.call('exists', KEYS[1]) == 0)
向map类型中添加数据, key为客户端id ,value 为 1 ,表示当前锁重入的个数为1
then redis.call('hset', KEYS[1], ARGV[2], 1);
设置过期时间 ,默认为30秒
redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end;
//如果存在该锁,并且该锁的持有者和当前客户端id相同
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1)
//将重入的个数加 1
then redis.call('hincrby', KEYS[1], ARGV[2], 1);
redis.call('pexpire', KEYS[1], ARGV[1]);
返回0
return nil; end;
//上述都不符合的话,就返回该key的过期时间
return redis.call('pttl', KEYS[1]);
key1 是要加锁的key
argv1 锁的key默认生存时间
argv2 加锁的客户端id,为生成的 UUID + thread_id ,UUID会在redission客户端对象创建的时候生成
释放锁:
0代表释放失败,1代表释放成功
//如果锁不存在
if (redis.call('exists', KEYS[1]) == 0)
//发送释放锁的消息,返回1
then redis.call('publish', KEYS[2], ARGV[1]); return 1; end;
//如果持有该锁的客户端id 不是当前id ,释放锁失败
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end;
//如果锁的持有者和当前客户端id一致,将可重如个数-1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
//如果当前可重入锁的个数大于0,说明锁不可以被释放
if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); return 0;
//如果当前可重入锁的个数为0,说明锁可以被释放,并进行消息的发送
else redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]); return 1; end;
//否则返回null
return nil;
详细信息可以查看 分布式锁
zookepper 实现分布锁: