可重入锁
# 可重入锁原理
ReentrantLock:记录当前为哪个线程,记录当前线程获取次数;
Redisson:使用hash数据结构,key为锁名称,value的k为线程标识,v为重入次数;
tryLock多少次,就要unlock多少次;
RLock lock = redissonClient.getLock("lock"); void method1(){ boolean isLock = lock.tryLock(); if(!isLock){ return ; } try{ method2(); }finally{ lock.unlock(); } } void method2(){ boolean isLock = lock.tryLock(); if(!isLock){ return ; } try{ }finally{ lock.unlock(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 流程图
# 加锁LUA脚本
Local key = KEYS[1];--锁的key
local threadId = ARGV[1];--线程唯一标识
local releaseTime = ARGV[2];--锁的自动释放时间
-- 判断是否存在
if(redis.call('exists',key) == 0) then
-- 不存在,获取锁
redis.call('hset',key,threadId,'1');
-- 设置有效期
redis.call('expire',key,releaseTime);
return 1;--返回结果
end
-- 锁已经存在,判断hreadId,是否是自己
if(redis.call('hexists',key,threadId) == 1) then
-- 不存在,获取锁重入次数+1
redis.call('hincrby',key,threadId,'1);
-- 设置有效期
redis.call('expire',key,releaseTime);
return 1;-- 返回结果
end
return 0;-- 代码走到这里,说明获取锁的不是自己,获取锁失收
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 释放锁LUA脚本
Local key KEYS[1];-- key
Local threadId = ARGV[1];-- 线程唯一标识
local releaseTime = ARGV[2];-- 锁的自动释放时间
-- 判断当前锁是否还是被自己持有
if (redis.call('HEXISTS',key,threadId) == 0) then
return nil;-- 如果已经不是自己,则直接返回
end
-- 是自己的锁,则重入次数-1
Local count redis.call('HINCRBY',key,threadId,-1);
-- 判断是否重入次数是否已经为0
if (count 0) then
-- 大于0说明不能释放锁,重置有效期然后返回
redis.call('EXPIRE',key,releaseTime);
return nil;
eLse -- 等于0说明可以释放锁,直接删除
redis.call('DEL',key);
return nil;
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18