基于Redis实现分布式锁——Java版本
基于Redis实现分布式锁——Java版本
- 版本一
- 版本二
- 版本三
- Redisson
定义分布式锁接口如下:
public interface ILock {boolean tryLock(long timeoutSec);void unlock();
}
版本一
设定业务超时时间,到期自动解锁。缺点是超时时间不好估计,需要略大于业务执行的时间。当超时时间小于执行业务时间时,其他线程会拿到锁,而之前的线程执行完后又会解锁,变得混乱,导致线程安全问题。
public class SimpleRedisLock implements ILock{@Autowiredprivate StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX = "lock:";@Overridepublic boolean tryLock(long timeoutSec) {Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, Thread.currentThread().getId() + "",timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {stringRedisTemplate.delete(KEY_PREFIX + name);}
}
版本二
解锁时判断锁是否和自己假的锁标识一样,标识使用UUID+线程ID,标识一样才释放锁。每个线程都会创建一个SimpleLock,因此保证UUID不一样。
public class SimpleRedisLock implements ILock{private StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";public SimpleRedisLock(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;}@Overridepublic boolean tryLock(long timeoutSec) {String value = ID_PREFIX + Thread.currentThread().getId();Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, value,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {String value = ID_PREFIX + Thread.currentThread().getId();String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);if (value.equals(id)) {stringRedisTemplate.delete(KEY_PREFIX + name);}}
}
版本三
某个线程先判断锁是自己的,此时由于其他原因阻塞,比如Full GC,其他线程拿到锁,之前的线程再解锁,但是解的并不是自己的锁,导致线程安全问题。
需要保证这些操作的原子性。使用Lua脚本。
使用redis提供的函数call。key类型参数放入KEYS数组,其他参数放入ARGV数组,Lua中数组角标从1开始。Lua脚本如下。
if (redis.call('get', KEYS[1]) == ARGV[1]) thenreturn redis.call('del', KEYS[1])
end
return 0
Lua脚本放在resources文件夹下,在Java代码中调用StringRedisTemplate的execute方法执行Lua脚本。最后分布式锁代码为
public class SimpleRedisLock implements ILock{private StringRedisTemplate stringRedisTemplate;private String name;private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";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(StringRedisTemplate stringRedisTemplate, String name) {this.stringRedisTemplate = stringRedisTemplate;this.name = name;}@Overridepublic boolean tryLock(long timeoutSec) {String value = ID_PREFIX + Thread.currentThread().getId();Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, value,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}@Overridepublic void unlock() {stringRedisTemplate.execute(UNLOCK_SCRIPT,Collections.singletonList(KEY_PREFIX + name),ID_PREFIX + Thread.currentThread().getId());}
}
Redisson
上述实现的分布式锁缺点为:
1、不可重入,同一线程不能对同一把锁多次加锁。
2、不可重试。
3、超时时间不好设置,有可能超时自动释放,虽然不会有误删,但是存在其他线程重新加锁。
4、主从复制的单点问题,主节点宕机导致从节点锁还没有同步。
这些功能属于拓展功能,要么出现概率低,要么可以不需要这样的需求。
Redisson包含分布式锁的成熟实现。
1、Redisson的可重入锁实现原理:
参考ReentrantLock原理,需要存储加锁次数。因此使用Redis中的Hash数据结构。key是锁名称,field是UUID+线程id,value是加锁次数。
2、Redisson的可重试锁和超时释放实现原理:
while持续在重试时间内重试,但不是一直重试,而是消息订阅和信号量,释放了再来重试。
超时释放使用了看门狗机制,每10秒钟续期30秒,无限续期,直到调用unLock方法。
3、Redisson解决主从一致性问题的原理:
去中心化,不要主从,每个节点都需要获取锁,使用了红锁算法。N个节点需要获取N/2+1个锁才能加锁成功。使用Multilock。
缺点是增加读写。
对每个节点都使用配置类把Bean加载到容器中。
RLock lock1 = redissonClient.getLock("order");
RLock lock2 = redissonClient2.getLock("order");
RLock lock3 = redissonClient3.getLock("order");
RLock lock = redissonClient.getMultiLock(lock1, lock2, lock3);
相关文章:
基于Redis实现分布式锁——Java版本
基于Redis实现分布式锁——Java版本 版本一版本二版本三Redisson 定义分布式锁接口如下: public interface ILock {boolean tryLock(long timeoutSec);void unlock(); }版本一 设定业务超时时间,到期自动解锁。缺点是超时时间不好估计,需要…...
Qt自定义控件--提升为
为什么要自定义控件 1,有复合小控件需要组合为一个整体控件时; 2,一个复合控件需要重复使用时; 实现 自定义控件文件 新增三个文件 关联不同组的控件 关联之前的准备工作 1,在主控件选择和子控件所有控件所在控件…...
Lua 基础 01 入门
Lua 基础相关知识 第一期 注释 -- 单行注释--[[多行注释 --]]-- 多加一个横杠符号就能重新启用注释内的代码 ---[[print("Lua") --]]数据类型 Lua 是动态类型语言,变量不需要类型定义,只需要为变量赋值。 Lua 有 8 种基本类型:…...
远程连接阿里云ECS
说明:ECS(阿里云服务器)可选择的系统镜像如下: 本文介绍基于Windows系统,对CentOS、Ubuntu、Windows这三个操作系统的连接方式,以及连接工具Windterm的使用。 CentOS & Windterm CentOS是我使用时间最…...
【C++】多态(上)超详细
封装,继承,多态不只是C的三大特性,而是面向对象编程的三大特性。 什么是多态: 不同的对象做同一件事情,结果会出现多种形态。 1.满足多态的几个条件 1.父子类完成虚函数重写(需要满足三同:函…...
【Git】 Git分支操作指南
隐形的纪念躲在心里面 也许吧 也许不会再见 阴天或晴天 一天又一年 风它在对我说莫忘这一切 🎵 蔡淳佳《隐形纪念》 Git是一种非常强大的分布式版本控制系统,允许用户在开发过程中创建不同的分支(branch)来分…...
智慧文旅赋能旅游服务升级:以科技创新驱动行业变革,打造智慧化、个性化、高效化的旅游新体验,满足游客日益增长的多元化需求
目录 一、引言 二、智慧文旅的概念与内涵 三、智慧文旅在旅游服务升级中的应用 1、智慧旅游服务平台建设 2、智慧景区管理 3、智慧旅游营销 四、智慧文旅推动旅游行业变革的案例分析 案例一:某智慧旅游城市建设项目 案例二:某景区智慧化改造项目…...
AtCoder Beginner Contest 310 E题 NAND repeatedly
E题:NAND repeatedly 标签:动态规划题意:给定一个长度为 n n n的 01 01 01字符串 A i A_i Ai,给定规则: 0 ⊼ 0 1 , 0 ⊼ 1 1 , 1 ⊼ 0 1 , 1 ⊼ 1 0 0⊼01,0⊼11,1⊼01,1⊼10 0⊼01,0⊼11,1⊼01,1⊼10。 求 ∑…...
一款简易的免费抽奖软件
一、介绍 这款抽奖软件设计简洁,操作便捷。用户可以轻松将参与名单通过EXCEL文件导入至程序中,并可根据需要设定各类奖品和对应的中奖人数。在选定了奖品后,用户只需点击“开始”按钮,随后再按下“暂停”按钮,软件便会…...
Kubernetes 监控管理
目录 1. Metrics Server2. Prometheus & Grafana3. cAdvisor4. 日志收集5. 告警与通知6. 最佳实践 Kubernetes 监控管理是确保集群稳定运行和应用服务质量的关键环节。它涉及收集、聚合、分析集群及其上运行的应用程序的各种指标和日志数据。 1. Metrics Server 作用&…...
哈希表第6/9题--四数相加II
题目描述: 给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足: 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1: 输入&…...
使用JavaScript将富文本HTML转换为纯文本
在Web开发中,我们经常需要处理HTML内容,但有时为了特定的目的,比如文本处理、搜索或显示在非HTML环境中,我们可能希望将富文本HTML转换为纯文本。这里,我们将探讨如何使用JavaScript来实现这一功能。 为什么要将HTML转…...
2024-05-13 问AI: 介绍一下 google wavenet 声码器
文心一言 Google的WaveNet声码器是一个深度学习模型,用于生成高质量的音频信号,特别是人类语音。与传统的声码器相比,WaveNet可以生成更加自然和流畅的音频,因为它直接模拟了原始音频信号的波形生成过程。 WaveNet的核心思想是使…...
当代 Qt 正确的 安装方法 及 多版本切换
此文写于 20240511 首先去网站Index of /official_releases/online_installers下载一个安装器 安装器有什么用? 可以浏览安装版本 安装组件 安装器版本越能 能装的东西越多 现在只能选Qt5 和 Qt6 至于你公司用的Qt4 我也没招 见招时再拆招 安装器 默认国外源 可以换国内…...
matlab使用教程(70)—修改坐标区属性
1.控制坐标轴长度比率和数据单位长度 您可以控制 x 轴、y 轴和 z 轴的相对长度(图框纵横比),也可以控制一个数据单位沿每个轴的相对长度(数据纵横比)。 1.1图框纵横比 图框纵横比是 x 轴、y 轴和 z 轴的相对长度。默认…...
手撕C语言题典——反转链表
目录 前言 一.思路 1)创建新链表 2)创建三个指针 二.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识,我们来尝试做一下关于顺序表的经典算法题~ 前言 反转…...
用lobehub打造一个永久免费的AI个人助理
Lobe Chat是一个开源的高性能聊天机器人框架,它被设计来帮助用户轻松创建和部署自己的聊天机器人。这个框架支持多种智能功能,比如语音合成(就是让机器人能说话),还能理解和处理多种类型的信息,不仅限于文字…...
Linux网络编程】传输层中的TCP和UDP(UDP篇)
【Linux网络编程】传输层中的TCP和UDP(UDP篇) 目录 【Linux网络编程】传输层中的TCP和UDP(UDP篇)传输层再谈端口端口号范围划分认识知名端口号netstatiostatpidofxargs UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲数据UDP使…...
Ciphey无法安装的解决办法
安装过程纯属自己实践,满满干货 困扰我几天的问题终于解决了 我看着教程在window上安装 python3.8/python3.9/python3.10无论如何都安装不上, 在win10虚拟机仍然安装不上 可能是我电脑环境问题 解决办法: 在kali中安装,但是…...
交互之舞:Processing中的用户互动与响应设计
前言: 🌟在前两篇文章中,我们已经学会了如何绘制静态图形和创建动态动画。今天,我们将迈入一个新的领域——交互设计。在Processing中,用户互动是创造沉浸式体验的关键。让我们一起探索如何让用户与你的艺术作品互动&…...
别再只会用MI了!深入对比PLV、MVL、MI:在Python中如何为你的EEG数据选择最佳跨频耦合算法
别再只会用MI了!深入对比PLV、MVL、MI:在Python中如何为你的EEG数据选择最佳跨频耦合算法 脑电信号分析中,跨频耦合(Cross-Frequency Coupling, CFC)已成为揭示神经活动协调机制的重要工具。面对PLV、MVL、MI这三种主流…...
别再傻傻串联了!聊聊数字电路里移位器的三种实现:从简单开关到桶形和对数结构
数字电路设计中的移位器架构选择:从基础实现到性能优化 在数字电路设计中,移位操作是最基础却又最容易被低估的功能之一。许多刚入行的工程师往往会采用最简单的串联移位结构,直到项目遇到性能瓶颈才开始思考优化方案。实际上,移…...
DDrawCompat开源项目:让Windows经典游戏在现代系统重生
DDrawCompat开源项目:让Windows经典游戏在现代系统重生 【免费下载链接】DDrawCompat DirectDraw and Direct3D 1-7 compatibility, performance and visual enhancements for Windows Vista, 7, 8, 10 and 11 项目地址: https://gitcode.com/gh_mirrors/dd/DDraw…...
无王无帝定乾坤,来自田间第一人 凰标传世照千秋
无王无帝定乾坤 ——来自田间第一人华夏文明千年流转,王朝霸业此起彼伏。 无数帝王功业随岁月风化,无数朝堂规制随朝代更迭消散。 真正能够跨越岁月、贯穿古今、安定世道、照亮千秋的, 从不是一时的权位霸业,而是亘古不变的公道正…...
手把手教你用YOLACT训练自己的数据集:从COCO格式准备到模型推理全流程(附Python源码)
YOLACT实战指南:从数据标注到工业级实例分割模型部署 1. 实例分割技术演进与YOLACT核心优势 在计算机视觉领域,实例分割一直被视为目标检测与语义分割的结合体。不同于简单的边界框检测或像素级分类,实例分割要求算法能够区分同一类别的不同个…...
CVE-2026-42897深度解析:Exchange零日XSS武器化全链条与企业防御实战指南
摘要:2026年5月14日,微软紧急披露Exchange Server高危零日漏洞CVE-2026-42897,该漏洞无需任何前置权限,仅通过一封恶意邮件即可在OWA界面触发任意JavaScript执行。截至5月19日,全球已有超过1200台Exchange服务器被观测…...
CANape测量启动报错“存储空间不足”的系统性排查与解决方案
1. 问题现象与根源剖析如果你是一名汽车电子工程师,或者从事车辆标定、诊断与测试工作,那么CANape这个软件对你来说,就像吃饭用的筷子一样熟悉。它强大的测量、标定和诊断功能,是我们在开发过程中不可或缺的利器。然而,…...
从硬件连接到数据可视化:基于RS485-USB的传感器数据采集全流程解析
1. 硬件连接:从传感器到电脑的物理链路搭建 工业传感器数据采集的第一步,就是建立可靠的物理连接。以常见的星仪压力变送器为例,我们需要解决三个关键问题:传感器供电、信号传输转换、以及电脑端识别。这里我分享几个实际项目中容…...
JetBrains IDE试用期重置终极指南:如何免费延长30天评估期
JetBrains IDE试用期重置终极指南:如何免费延长30天评估期 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter JetBrains IDE试用期重置工具(ide-eval-resetter)是一款专业的开源解…...
用C++模拟堆宝塔游戏:PTA L2-045题解与STL vector实战
用C模拟堆宝塔游戏:PTA L2-045题解与STL vector实战 堆宝塔游戏是一个有趣的逻辑挑战,它要求玩家根据彩虹圈的直径大小,按照特定规则将它们堆叠成宝塔。这个游戏不仅考验玩家的逻辑思维能力,还能帮助我们深入理解C中STL容器的使用…...
