基于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中,用户互动是创造沉浸式体验的关键。让我们一起探索如何让用户与你的艺术作品互动&…...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
Java多线程实现之Callable接口深度解析
Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式
简介 在我的 QT/C 开发工作中,合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式:工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...
算法250609 高精度
加法 #include<stdio.h> #include<iostream> #include<string.h> #include<math.h> #include<algorithm> using namespace std; char input1[205]; char input2[205]; int main(){while(scanf("%s%s",input1,input2)!EOF){int a[205]…...
无需布线的革命:电力载波技术赋能楼宇自控系统-亚川科技
无需布线的革命:电力载波技术赋能楼宇自控系统 在楼宇自动化领域,传统控制系统依赖复杂的专用通信线路,不仅施工成本高昂,后期维护和扩展也极为不便。电力载波技术(PLC)的突破性应用,彻底改变了…...