当前位置: 首页 > news >正文

Redis实现可重入锁

什么是可重入锁?

可重入锁(Reentrant Lock)是指同一个线程可以多次获取同一把锁而不会造成死锁的一种锁机制。它具有以下特点:

  1. 可重入性:同一个线程可以重复获取已经持有的锁

  2. 锁计数:内部维护一个计数器,记录锁被获取的次数

  3. 正确释放:必须释放与获取次数相同的次数才能真正释放锁

可重入锁避免了线程因重复获取自己持有的锁而导致死锁的情况,是分布式系统中常用的锁机制。

实现原理

Redis实现可重入锁主要依赖:

  1. SET key value NX PX milliseconds 命令实现原子性加锁

  2. Lua脚本保证解锁的原子性

  3. 使用线程标识和计数器实现可重入

代码实现

 RedisReentrantLock:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;public class RedisReentrantLock {private final Jedis jedis;private final String lockKey;private final String lockValue;private final long expireTime; // 锁过期时间(毫秒)// 线程局部变量,存储锁计数器private static final ThreadLocal<Map<String, Integer>> LOCK_COUNTER = ThreadLocal.withInitial(HashMap::new);public RedisReentrantLock(Jedis jedis, String lockKey, long expireTime) {this.jedis = jedis;this.lockKey = lockKey;this.lockValue = UUID.randomUUID().toString() + ":" + Thread.currentThread().getId();this.expireTime = expireTime;}/*** 获取锁* @param waitTime 等待时间(毫秒)* @return 是否获取成功* @throws InterruptedException*/public boolean tryLock(long waitTime) throws InterruptedException {Map<String, Integer> counterMap = LOCK_COUNTER.get();Integer count = counterMap.get(lockKey);// 如果已经持有锁,增加计数并返回成功if (count != null && count > 0) {counterMap.put(lockKey, count + 1);return true;}long endTime = System.currentTimeMillis() + waitTime;while (System.currentTimeMillis() < endTime) {// 尝试获取锁String result = jedis.set(lockKey, lockValue, SetParams.setParams().nx().px(expireTime));if ("OK".equals(result)) {counterMap.put(lockKey, 1); // 初始化计数器return true;}// 检查是否是当前线程持有的锁(锁重入)String currentValue = jedis.get(lockKey);if (lockValue.equals(currentValue)) {counterMap.put(lockKey, 1);return true;}Thread.sleep(100); // 等待一段时间后重试}return false;}/*** 释放锁*/public void unlock() {Map<String, Integer> counterMap = LOCK_COUNTER.get();Integer count = counterMap.get(lockKey);if (count == null || count <= 0) {throw new IllegalStateException("未持有锁,不能释放");}// 减少计数int newCount = count - 1;counterMap.put(lockKey, newCount);// 如果计数为0,真正释放锁if (newCount == 0) {// 使用Lua脚本保证原子性String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"   return redis.call('del', KEYS[1]) " +"else " +"   return 0 " +"end";jedis.eval(luaScript, 1, lockKey, lockValue);counterMap.remove(lockKey);}}
}

使用示例:

public class LockExample {public static void main(String[] args) {Jedis jedis = new Jedis("localhost", 6379);RedisReentrantLock lock = new RedisReentrantLock(jedis, "order_lock", 30000);try {// 尝试获取锁,等待5秒if (lock.tryLock(5000)) {try {System.out.println("第一次获取锁成功");// 可重入测试if (lock.tryLock(5000)) {try {System.out.println("第二次获取锁成功(可重入)");// 执行业务逻辑processOrder();} finally {lock.unlock();}}} finally {lock.unlock();}} else {System.out.println("获取锁失败");}} catch (InterruptedException e) {Thread.currentThread().interrupt();System.out.println("线程被中断");} finally {jedis.close();}}private static void processOrder() {// 订单处理逻辑System.out.println("处理订单中...");}
}

http://www.dtcms.com/a/315783.html

相关文章:

  • “Why“比“How“更重要:层叠样式表CSS
  • 《C++初阶之STL》【模板参数 + 模板特化 + 分离编译】
  • @【JCIDS】【需求论证】联合能力集成与开发系统知识图谱
  • 机器学习通关秘籍|Day 03:决策树、随机森林与线性回归
  • 【工程化】tree-shaking 的作用以及配置
  • Android Framework代码屏蔽未接来电振动及声音通知
  • DHTMLX重磅发布React Scheduler组件,赋能日程管理开发!
  • SELinux加固Linux安全
  • 将普通用户添加到 Docker 用户组
  • 第十七天:原码、反码、补码与位运算
  • RAFT:让语言模型更聪明地用文档答题
  • Java从入门到精通 - 集合框架(一)
  • 最长连续序列(每天刷力扣hot100系列)
  • FastDeploy2.0:报qwen2.embed_tokens.weight
  • 2.4 组件通信
  • 24. 前端-js框架-Vue
  • Occ3D: A Large-Scale 3D Occupancy Prediction Benchmark for Autonomous Driving
  • Python高级编程与实践:Python性能分析与优化
  • Java技术栈/面试题合集(3)-Java并发篇
  • 【功能测试】软件功能上线测试经验总结
  • 场外个股期权的额度为何受限?
  • java web 服务员点餐系统demo 比较完整的前端后端+mysql + 图片上传 练习
  • 从审批流到审计链:刻录系统的全周期管控技术解析
  • Spring MVC框架中DispatcherServlet详解
  • 【开源工具】基于Python的PDF清晰度增强工具全解析(附完整源码)
  • LeetCode算法日记 - Day 2: 快乐数、盛水最多容器
  • 力扣经典算法篇-43-全排列(经典回溯问题)
  • vite面试题及详细答案120题(01-30)
  • 普通树状数组
  • 《Node.js与 Elasticsearch的全文搜索架构解析》