【Redis】Redisson的可重入锁原理

张开发
2026/4/13 13:46:40 15 分钟阅读

分享文章

【Redis】Redisson的可重入锁原理
目录一. 什么是可重入锁二、怎么实现的“重入”三、Redisson可重入锁怎么用第一步准备环境第二步写核心代码第三步测试测试结果四、注意事项五、总结一. 什么是可重入锁假设你去网吧上网网吧里的每台电脑比如1号机就是“一把锁”开机密码或身份证刷卡就是“拿锁的权限”你就是程序里的“线程”——只有你刷了卡、开了机才能用这台电脑别人不能用这就是“锁”的作用保证同一时间只有一个线程能操作资源。场景1可重入正常情况你刷身份证开机用1号机相当于“线程拿到锁”玩了一会儿想去厕所相当于程序里“一个方法调用另一个方法两个方法都需要同一把锁”你不用关机、释放1号机不用释放锁直接起身去厕所回来后还能直接坐回1号机、继续玩——这就是“可重入”怎么拿、怎么用不卡壳不用等自己“释放”电脑也不会卡死。场景2不可重入卡死情况如果这台电脑是“不可重入”的你开机用了1号机后想去厕所必须先关机、释放1号机释放锁才能起身等你从厕所回来想再用1号机还得重新刷卡开机重新拿锁。可你根本不想关机业务还没做完一旦关机之前玩的内容就没了不关机又没法起身——这就是程序里的“死锁”程序直接卡死没法继续运行。对应到程序里再简单说一句你写了两个方法A和B两个方法都需要同一把锁相当于都需要用1号机方法A里会调用方法B相当于用1号机时中途要做另一个需要用1号机的操作。用Redisson可重入锁程序执行A、再调用B时能直接执行不卡顿用不可重入锁程序会卡在B那里自己等自己释放锁永远等不到直接卡死。二、怎么实现的“重入”可重入锁主要用计数器实现的。计数器的唯一作用就是记着你“拿了几次锁”避免“释放一次就把锁彻底放走”也避免自己卡死自己全程由Redisson自动管理我们不用手动操作。具体工作流程你第一次刷身份证开机用1号机线程第一次获取锁Redisson的计数器就记1相当于“你第一次占用1号机”你玩到中途想重启电脑相当于程序里“方法A调用方法B再次获取同一把锁”因为是你本人同一个线程Redisson不用让你重新刷卡直接把计数器加1变成2相当于“你第二次占用1号机”重启完成你继续玩相当于methodB执行完释放一次锁计数器就减1变成1相当于“你释放了一次占用但还在使用1号机”你玩完了关机离开相当于methodA执行完再释放一次锁计数器减1变成0相当于“你彻底释放1号机”只有计数器变成0这把锁才真正放开其他人才可以刷身份证、开机用1号机其他线程才能获取这把锁。重点提醒如果没有计数器会出什么问题就像你在网吧重启电脑释放一次锁后计数器没记录系统会误以为你“彻底离开”直接把1号机释放别人可能趁你还在座位上就刷卡开机——对应程序里就是“锁提前释放”多个线程同时操作一个资源比如同时改一个数据会导致数据错乱、程序出问题。补充一句Redisson已经帮我们把“计数器”“线程标识”识别是不是同一个线程拿锁全部封装好了我们不用自己写一行代码管理计数器只需要调用简单的lock()拿锁和unlock()释放锁就能实现可重入这也是Redisson比其他Redis客户端好用的地方。三、Redisson可重入锁怎么用第一步准备环境1. 给项目加依赖dependency groupIdorg.redisson/groupId artifactIdredisson-spring-boot-starter/artifactId version3.23.3/version /dependency2. 配置Redis复制到application.yml里只改2个地方Redis地址、Redis密码没有密码就删掉密码那一行spring: redis: host: 127.0.0.1 # 改成你自己的Redis地址本地开发就是127.0.0.1 port: 6379 # 默认端口一般不用改 password: 123456 # 改成你自己的Redis密码没有就删掉这一行 redisson: singleServerConfig: address: redis://127.0.0.1:6379 # 和上面的Redis地址、端口一致 password: 123456 # 和上面的Redis密码一致没有就删掉这一行第二步写核心代码我们写一个服务类里面有两个方法methodA和methodBmethodA调用methodB两个方法都用同一把锁模拟“重入”场景对应网吧里“开机后重启电脑”的场景代码里的注释都写得很直白一看就懂直接复制粘贴到项目里就行import org.redisson.api.RLock; import org.redisson.api.RedissonClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; // 这个注解不用管加了就能用 Service public class LockDemo { // 注入Redisson不用管怎么来的加了依赖就能自动注入 Autowired private RedissonClient redissonClient; // 锁的名字必须唯一相当于网吧1号机的“编号”两个方法必须用同一个名字 private static final String LOCK_NAME myLock; // 方法A需要拿锁相当于网吧开机用1号机 public void methodA() { // 1. 获取可重入锁拿锁就是刷身份证拿1号机的使用权 RLock lock redissonClient.getLock(LOCK_NAME); try { // 2. 加锁开机不用管其他的调用这个方法就好 lock.lock(); System.out.println(程序线程进入methodA拿到锁了相当于开机用1号机); // 3. 调用methodBmethodB也需要同一把锁相当于开机后重启电脑再次占用1号机 methodB(); // 模拟业务操作比如存数据、改数据不用改照搬就行 Thread.sleep(1000); } catch (Exception e) { // 出错了也不用管这里只是防止程序崩溃 e.printStackTrace(); } finally { // 4. 释放锁关机必须写在这里防止程序出错后锁没释放 lock.unlock(); System.out.println(程序线程退出methodA释放锁了相当于关机离开1号机); } } // 方法B也需要拿同一把锁相当于重启电脑再次占用1号机 public void methodB() { // 还是同一把锁LOCK_NAME和上面一样相当于还是1号机 RLock lock redissonClient.getLock(LOCK_NAME); try { // 加锁重启电脑因为是同一个程序直接重入不用等 lock.lock(); System.out.println(程序线程进入methodB重入锁成功相当于重启电脑继续用1号机); // 模拟业务操作不用改 Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } finally { // 释放锁重启完成计数器减1不是彻底释放 lock.unlock(); System.out.println(程序线程退出methodB释放重入锁相当于重启完成继续使用1号机); } } }第三步测试写一个简单的测试接口调用上面的methodA看控制台输出就能知道可重入锁有没有生效代码还是复制粘贴import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; // 测试接口不用改 RestController public class TestLockController { // 注入上面写的LockDemo类 Autowired private LockDemo lockDemo; // 访问这个接口就能测试可重入锁 GetMapping(/testLock) public String testLock() { // 调用methodA会自动调用methodB模拟重入场景 lockDemo.methodA(); return 测试成功去看控制台输出; } }测试结果启动项目访问接口http://localhost:8080/testLock打开控制台会看到下面的输出顺序不能乱程序线程进入methodA拿到锁了相当于开机用1号机 程序线程进入methodB重入锁成功相当于重启电脑继续用1号机 程序线程退出methodB释放重入锁相当于重启完成继续使用1号机 程序线程退出methodA释放锁了相当于关机离开1号机解读一下这个结果程序先进入methodA、拿到锁开机用1号机然后直接进入methodB重入成功相当于重启电脑不用重新刷卡再依次释放methodB、methodA的锁重启完成→关机离开——这就证明可重入锁生效了Redisson的计数器也在正常工作拿2次、放2次最后彻底释放。四、注意事项同一把锁的名字必须一致相当于网吧里“1号机器”的锁你开机用了1号机拿锁中途去厕所回来还想坐1号机重入锁必须找的是“1号机”的锁不能找“2号机”的锁——对应代码里methodA和methodB用的LOCK_NAME必须都是“myLock”不能一个写“myLock”、一个写“myLock1”否则不是同一把锁没法重入。lock()和unlock()要成对出现相当于你去网吧开机lock()拿锁、关机unlock()释放锁要对应。你开了一次机就必须关一次机开两次机比如开机后又重启一次相当于重入就必须关两次机。多关一次多unlock()系统会报错少关一次少unlock()这台机器会一直显示“已占用”别人没法用锁泄漏。unlock()必须写在finally里相当于你在网吧上网不管是正常关机业务正常执行还是突然停电、电脑死机程序出错机器都会自动释放关机不会一直占着位置。如果不写在finally里程序一出错就没人“关机”这台机器永远被占用别人用不了。五、总结1. 可重入锁你在网吧坐1号机拿锁中途去厕所、重启电脑重入回来还能直接坐1号机不用等自己“释放”机器不卡顿2. Redisson核心原理靠“计数器”记拿锁次数拿一次加1放一次减1减到0才彻底释放锁避免提前释放和死锁

更多文章