redisson重入锁的原理
什么是重入锁?
一个线程可以多次获取同一把锁。
为什么要使用同一把锁?
有很多的原型,这里我举一个例子。
在特殊的业务中,一个方法一调用方法二,两个方法调用之前需要获取这一把锁,如果锁获取不到之后需要等待,那么,调用方法一的时候获取这把锁,调用方法二的之后获取不到,这个时候就产生了死锁。
怎么设计重入锁?
这个不可重入锁
/**
* @ClassName: SimpleRedisLock
* @Description: 初级的redis分布式锁
* @Author: csh
* @Date: 2025-02-19 15:05
*/
public class SimpleRedisLock implements ILock{
private StringRedisTemplate stringRedisTemplate;
private String name;
private static final String KEY_PREFIX = "lock:";
private static final String UUID_PREFIX = UUID.randomUUID().toString(true)+"-";
private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
static{
UNLOCK_SCRIPT = new DefaultRedisScript<>();
UNLOCK_SCRIPT.setLocation(new ClassPathResource("unlock.lua"));
UNLOCK_SCRIPT.setResultType(Long.class);
}
public SimpleRedisLock(String name , StringRedisTemplate stringRedisTemplate ) {
this.stringRedisTemplate = stringRedisTemplate;
this.name = name;
}
/**
* @param timeoutSec
* @description: 尝试获取锁
* @param: timeoutSec 锁过期时间
* @return: boolean
* @author: csh
* @date: 2025/2/19
*/
@Override
public boolean tryLock(long timeoutSec) {
// 获取线程提示
String threadId = UUID_PREFIX + Thread.currentThread().getId();
// 获取线程锁
Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, timeoutSec, TimeUnit.SECONDS);
return Boolean.TRUE.equals(success);
}
/**
* @description: 释放锁
* @param:
* @return: void
* @author: csh
* @date: 2025/2/19
*/
@Override
public void unlock() {
// 调用lua脚本
stringRedisTemplate.execute(
UNLOCK_SCRIPT,
// 获取锁的key
Collections.singletonList(KEY_PREFIX + name),
// 获取线程id
UUID_PREFIX + Thread.currentThread().getId()
);
}
}
自己定义的锁使用的是redis的setnx,只有在key不存在的情况下才能设置,意味着如果有线程获取到这把锁,后面的包括自己不能再次获取。
redisson实现了可重入锁,Rlock,实现的大概原理是,采用hash结构用来存储锁,其中大key表示表示这把锁是否存在,用小key表示当前这把锁被哪个线程持有,小key的值记录的同一线程获取锁的次数,同一个线程可以多次获取同一把锁,每次获取计数器加一。
实现原理如下:
获取:
释放: