一、概述
Redisson
可重入锁是一种分布式锁,它基于 Redis
实现。可重入指的是同一个线程在持有锁的情况下,可以多次获取该锁而不会造成死锁。
-----它可以在分布式系统中用于实现互斥锁。这种锁可以允许多个线程同时获取锁,但在任何给定时间只有一个线程可以执行受保护的代码块。
-----Redission
锁提供了一种简单的方法来保证在分布式系统中的互斥性,同时支持可重入性。这意味着一个线程可以在获取锁之后再次获取同一个锁,而不需要等待锁释放。
二、原理
1、当线程 A
尝试获取锁时,它会向 Redis
服务器发送 SETNX
命令来尝试将锁的键设置为特定的值。如果 SETNX
命令返回成功,说明线程 A
成功获取了锁。
2、如果 SETNX
命令返回失败,说明锁的键已经被其他线程持有,这时 Redisson
会检查当前持有锁的线程是否是同一个线程。如果是,则允许该线程再次获取锁,并将锁的计数器加一。
3、如果 SETNX
命令返回失败且当前持有锁的线程不是同一个线程,则表示其他线程已经持有了锁,此时线程 A
需要等待。
4、线程 A
在等待期间会使用 Redis
的订阅与发布功能(Pub/Sub
)来订阅锁的释放事件。
5、当持有锁的线程释放锁时,Redis
服务器会发布一个锁释放的消息。线程 A
收到消息后会重新尝试获取锁。
通过以上的机制,
Redisson
实现了可重入锁的功能。它能够保证同一个线程可以多次获取锁,而其他线程在锁被持有期间会进入等待状态。
需要注意的是,为了避免死锁,线程必须释放它所持有的所有锁,即锁的计数器必须归零
。只有当锁的计数器为零时,其他线程才能获取该锁。
其中对于获得锁和释放锁,redisson底层也是采用了lua脚本来实现的,不仅实现了锁的可重用性,还保证了分布式锁的原子性
添加锁脚本
"if (redis.call('exists', KEYS[1]) == 0) then " +
"redis.call('hset', 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]);"
删除锁Lua脚本
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
return nil;
end ;
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);
if (counter > 0) then
redis.call('pexpire', KEYS[1], ARGV[2]);
return 0;
else
redis.call('del', KEYS[1]);
redis.call('publish', KEYS[2], ARGV[1]);
return 1;
end ;
return nil;
测试示例:
在分布式锁中,他采用hash结构用来存储锁,其中大key表示表示这把锁是否存在,用小key表示当前这把锁被哪个线程持有,value代表该线程锁的重数
上述图:
Key
------->大key 大key表示表示这把锁是否存在
field
------->小key 小key表示当前这把锁被哪个线程持有
value
------->value代表该线程锁的重数