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

基于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 定义分布式锁接口如下&#xff1a; public interface ILock {boolean tryLock(long timeoutSec);void unlock(); }版本一 设定业务超时时间&#xff0c;到期自动解锁。缺点是超时时间不好估计&#xff0c;需要…...

Qt自定义控件--提升为

为什么要自定义控件 1&#xff0c;有复合小控件需要组合为一个整体控件时&#xff1b; 2&#xff0c;一个复合控件需要重复使用时&#xff1b; 实现 自定义控件文件 新增三个文件 关联不同组的控件 关联之前的准备工作 1&#xff0c;在主控件选择和子控件所有控件所在控件…...

Lua 基础 01 入门

Lua 基础相关知识 第一期 注释 -- 单行注释--[[多行注释 --]]-- 多加一个横杠符号就能重新启用注释内的代码 ---[[print("Lua") --]]数据类型 Lua 是动态类型语言&#xff0c;变量不需要类型定义&#xff0c;只需要为变量赋值。 Lua 有 8 种基本类型&#xff1a…...

远程连接阿里云ECS

说明&#xff1a;ECS&#xff08;阿里云服务器&#xff09;可选择的系统镜像如下&#xff1a; 本文介绍基于Windows系统&#xff0c;对CentOS、Ubuntu、Windows这三个操作系统的连接方式&#xff0c;以及连接工具Windterm的使用。 CentOS & Windterm CentOS是我使用时间最…...

【C++】多态(上)超详细

封装&#xff0c;继承&#xff0c;多态不只是C的三大特性&#xff0c;而是面向对象编程的三大特性。 什么是多态&#xff1a; 不同的对象做同一件事情&#xff0c;结果会出现多种形态。 1.满足多态的几个条件 1.父子类完成虚函数重写&#xff08;需要满足三同&#xff1a;函…...

【Git】 Git分支操作指南

隐形的纪念躲在心里面 也许吧 也许不会再见 阴天或晴天 一天又一年 风它在对我说莫忘这一切 &#x1f3b5; 蔡淳佳《隐形纪念》 Git是一种非常强大的分布式版本控制系统&#xff0c;允许用户在开发过程中创建不同的分支&#xff08;branch&#xff09;来分…...

智慧文旅赋能旅游服务升级:以科技创新驱动行业变革,打造智慧化、个性化、高效化的旅游新体验,满足游客日益增长的多元化需求

目录 一、引言 二、智慧文旅的概念与内涵 三、智慧文旅在旅游服务升级中的应用 1、智慧旅游服务平台建设 2、智慧景区管理 3、智慧旅游营销 四、智慧文旅推动旅游行业变革的案例分析 案例一&#xff1a;某智慧旅游城市建设项目 案例二&#xff1a;某景区智慧化改造项目…...

AtCoder Beginner Contest 310 E题 NAND repeatedly

E题&#xff1a;NAND repeatedly 标签&#xff1a;动态规划题意&#xff1a;给定一个长度为 n n n的 01 01 01字符串 A i A_i Ai​&#xff0c;给定规则&#xff1a; 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。 求 ∑…...

一款简易的免费抽奖软件

一、介绍 这款抽奖软件设计简洁&#xff0c;操作便捷。用户可以轻松将参与名单通过EXCEL文件导入至程序中&#xff0c;并可根据需要设定各类奖品和对应的中奖人数。在选定了奖品后&#xff0c;用户只需点击“开始”按钮&#xff0c;随后再按下“暂停”按钮&#xff0c;软件便会…...

Kubernetes 监控管理

目录 1. Metrics Server2. Prometheus & Grafana3. cAdvisor4. 日志收集5. 告警与通知6. 最佳实践 Kubernetes 监控管理是确保集群稳定运行和应用服务质量的关键环节。它涉及收集、聚合、分析集群及其上运行的应用程序的各种指标和日志数据。 1. Metrics Server 作用&…...

哈希表第6/9题--四数相加II

题目描述&#xff1a; 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 < i, j, k, l < nnums1[i] nums2[j] nums3[k] nums4[l] 0 示例 1&#xff1a; 输入&…...

使用JavaScript将富文本HTML转换为纯文本

在Web开发中&#xff0c;我们经常需要处理HTML内容&#xff0c;但有时为了特定的目的&#xff0c;比如文本处理、搜索或显示在非HTML环境中&#xff0c;我们可能希望将富文本HTML转换为纯文本。这里&#xff0c;我们将探讨如何使用JavaScript来实现这一功能。 为什么要将HTML转…...

2024-05-13 问AI: 介绍一下 google wavenet 声码器

文心一言 Google的WaveNet声码器是一个深度学习模型&#xff0c;用于生成高质量的音频信号&#xff0c;特别是人类语音。与传统的声码器相比&#xff0c;WaveNet可以生成更加自然和流畅的音频&#xff0c;因为它直接模拟了原始音频信号的波形生成过程。 WaveNet的核心思想是使…...

当代 Qt 正确的 安装方法 及 多版本切换

此文写于 20240511 首先去网站Index of /official_releases/online_installers下载一个安装器 安装器有什么用? 可以浏览安装版本 安装组件 安装器版本越能 能装的东西越多 现在只能选Qt5 和 Qt6 至于你公司用的Qt4 我也没招 见招时再拆招 安装器 默认国外源 可以换国内…...

matlab使用教程(70)—修改坐标区属性

1.控制坐标轴长度比率和数据单位长度 您可以控制 x 轴、y 轴和 z 轴的相对长度&#xff08;图框纵横比&#xff09;&#xff0c;也可以控制一个数据单位沿每个轴的相对长度&#xff08;数据纵横比&#xff09;。 1.1图框纵横比 图框纵横比是 x 轴、y 轴和 z 轴的相对长度。默认…...

手撕C语言题典——反转链表

目录 前言 一.思路 1&#xff09;创建新链表 2&#xff09;创建三个指针 二.代码实现 搭配食用更佳哦~~ 数据结构之单单单——链表-CSDN博客 数据结构之单链表的基本操作-CSDN博客 前面学了单链表的相关知识&#xff0c;我们来尝试做一下关于顺序表的经典算法题~ 前言 反转…...

用lobehub打造一个永久免费的AI个人助理

Lobe Chat是一个开源的高性能聊天机器人框架&#xff0c;它被设计来帮助用户轻松创建和部署自己的聊天机器人。这个框架支持多种智能功能&#xff0c;比如语音合成&#xff08;就是让机器人能说话&#xff09;&#xff0c;还能理解和处理多种类型的信息&#xff0c;不仅限于文字…...

Linux网络编程】传输层中的TCP和UDP(UDP篇)

【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09; 目录 【Linux网络编程】传输层中的TCP和UDP&#xff08;UDP篇&#xff09;传输层再谈端口端口号范围划分认识知名端口号netstatiostatpidofxargs UDP协议UDP协议端格式UDP的特点面向数据报UDP的缓冲数据UDP使…...

Ciphey无法安装的解决办法

安装过程纯属自己实践&#xff0c;满满干货 困扰我几天的问题终于解决了 我看着教程在window上安装 python3.8/python3.9/python3.10无论如何都安装不上&#xff0c; 在win10虚拟机仍然安装不上 可能是我电脑环境问题 解决办法&#xff1a; 在kali中安装&#xff0c;但是…...

交互之舞:Processing中的用户互动与响应设计

前言&#xff1a; &#x1f31f;在前两篇文章中&#xff0c;我们已经学会了如何绘制静态图形和创建动态动画。今天&#xff0c;我们将迈入一个新的领域——交互设计。在Processing中&#xff0c;用户互动是创造沉浸式体验的关键。让我们一起探索如何让用户与你的艺术作品互动&…...

别再只会用MI了!深入对比PLV、MVL、MI:在Python中如何为你的EEG数据选择最佳跨频耦合算法

别再只会用MI了&#xff01;深入对比PLV、MVL、MI&#xff1a;在Python中如何为你的EEG数据选择最佳跨频耦合算法 脑电信号分析中&#xff0c;跨频耦合&#xff08;Cross-Frequency Coupling, CFC&#xff09;已成为揭示神经活动协调机制的重要工具。面对PLV、MVL、MI这三种主流…...

别再傻傻串联了!聊聊数字电路里移位器的三种实现:从简单开关到桶形和对数结构

数字电路设计中的移位器架构选择&#xff1a;从基础实现到性能优化 在数字电路设计中&#xff0c;移位操作是最基础却又最容易被低估的功能之一。许多刚入行的工程师往往会采用最简单的串联移位结构&#xff0c;直到项目遇到性能瓶颈才开始思考优化方案。实际上&#xff0c;移…...

DDrawCompat开源项目:让Windows经典游戏在现代系统重生

DDrawCompat开源项目&#xff1a;让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…...

无王无帝定乾坤,来自田间第一人 凰标传世照千秋

无王无帝定乾坤 ——来自田间第一人华夏文明千年流转&#xff0c;王朝霸业此起彼伏。 无数帝王功业随岁月风化&#xff0c;无数朝堂规制随朝代更迭消散。 真正能够跨越岁月、贯穿古今、安定世道、照亮千秋的&#xff0c; 从不是一时的权位霸业&#xff0c;而是亘古不变的公道正…...

手把手教你用YOLACT训练自己的数据集:从COCO格式准备到模型推理全流程(附Python源码)

YOLACT实战指南&#xff1a;从数据标注到工业级实例分割模型部署 1. 实例分割技术演进与YOLACT核心优势 在计算机视觉领域&#xff0c;实例分割一直被视为目标检测与语义分割的结合体。不同于简单的边界框检测或像素级分类&#xff0c;实例分割要求算法能够区分同一类别的不同个…...

CVE-2026-42897深度解析:Exchange零日XSS武器化全链条与企业防御实战指南

摘要&#xff1a;2026年5月14日&#xff0c;微软紧急披露Exchange Server高危零日漏洞CVE-2026-42897&#xff0c;该漏洞无需任何前置权限&#xff0c;仅通过一封恶意邮件即可在OWA界面触发任意JavaScript执行。截至5月19日&#xff0c;全球已有超过1200台Exchange服务器被观测…...

CANape测量启动报错“存储空间不足”的系统性排查与解决方案

1. 问题现象与根源剖析如果你是一名汽车电子工程师&#xff0c;或者从事车辆标定、诊断与测试工作&#xff0c;那么CANape这个软件对你来说&#xff0c;就像吃饭用的筷子一样熟悉。它强大的测量、标定和诊断功能&#xff0c;是我们在开发过程中不可或缺的利器。然而&#xff0c…...

从硬件连接到数据可视化:基于RS485-USB的传感器数据采集全流程解析

1. 硬件连接&#xff1a;从传感器到电脑的物理链路搭建 工业传感器数据采集的第一步&#xff0c;就是建立可靠的物理连接。以常见的星仪压力变送器为例&#xff0c;我们需要解决三个关键问题&#xff1a;传感器供电、信号传输转换、以及电脑端识别。这里我分享几个实际项目中容…...

JetBrains IDE试用期重置终极指南:如何免费延长30天评估期

JetBrains IDE试用期重置终极指南&#xff1a;如何免费延长30天评估期 【免费下载链接】ide-eval-resetter 项目地址: https://gitcode.com/gh_mirrors/id/ide-eval-resetter JetBrains IDE试用期重置工具&#xff08;ide-eval-resetter&#xff09;是一款专业的开源解…...

用C++模拟堆宝塔游戏:PTA L2-045题解与STL vector实战

用C模拟堆宝塔游戏&#xff1a;PTA L2-045题解与STL vector实战 堆宝塔游戏是一个有趣的逻辑挑战&#xff0c;它要求玩家根据彩虹圈的直径大小&#xff0c;按照特定规则将它们堆叠成宝塔。这个游戏不仅考验玩家的逻辑思维能力&#xff0c;还能帮助我们深入理解C中STL容器的使用…...