可重入原理分析
为什么自定义的Redis实现分布式锁不能实现可重入
method1中调用了method2方法,属于是同一线程内的操作,但是当method1中获取了锁之后,method2中就无法获取锁了,这就是同一线程无法实现可重入;
如何解决可以参考reentrantlock的原理;
ReentrantLock可以让线程排队获取锁并且支持重复获取。它的原理就像是一个排队等候的系统。
当一个线程想要获取锁时,它会检查锁是否已经被其他线程占用,也就是获取线程的标识。如果被占用(标识不相同),该线程会进入等待状态,排在一个队列中。只有当锁被释放时,下一个线程才能从队列中获取锁。
释放锁: ReentrantLock支持同一个线程再次获取锁,这称为重入。重入锁的意思是,当一个线程已经获取了锁,它可以再次获取这个锁,而不会被阻塞。每次重入操作会增加锁的计数器,当计数器变为0时,锁被完全释放,每个业务释放一次锁就减一,为0时说明在该线程下业务的最外层,才能释放锁。
所以在key-value结构中value就不能使用string类型,而是使用hash结构,记录锁的计数器;
之前的业务流程就需要修改;
其中在锁计数+1之后也需要重置锁的有效期,避免有效期到期过期无法执行后续业务;
测试代码
@Resource
private RedissonClient redissonClient;
private RLock lock;
@BeforeEach
void setUp(){
// 在每个测试方法之前执行的准备工作
// 可以在此初始化对象、加载数据等
lock = redissonClient.getLock("order");
}
@Test
void method1() {
boolean isLock = lock.tryLock();
if (!isLock) {
System.out.println("获取锁失败。。。1");
return;
}
try {
System.out.println("获取锁成功。。。1");
method2();
} finally {
System.out.println("释放锁。。。1");
lock.unlock();
}
}
void method2() {
boolean isLock = lock.tryLock();
if (!isLock) {
System.out.println("获取锁失败。。。2");
return;
}
try {
System.out.println("获取锁成功。。。2");
} finally {
System.out.println("释放锁。。。2");
lock.unlock();
}
}
解决可重入问题的核心思想:采用hash存储的结构,去记录一个hash存储的线程以及重入的次数,通过可重入数值计数该线程下的剩余业务继续获取锁以及在何时才能释放锁。