Redis实现分布式锁源码分析
为什么使用分布式锁
单机环境并发时,使用synchronized或lock接口可以保证线程安全,但它们是jvm层面的锁,分布式环境并发时,100个并发的线程可能来自10个服务节点,那就是跨jvm了。
简单分布式锁实现
SETNX
格式:setnx key value
当且仅当key不存在时,key能设置成功并返回1,否则返回0。
SETNX即SET if Not eXists 如果不存在。

SETNX存在的问题
高并发场景下,有可能存在key自动过期了,加锁的线程还未执行完成的情况。此时线程2加锁进来了,且刚好线程1执行完了,线程1会把线程2刚刚创建的锁删掉,导致后面进来了更多的线程。【自己加的锁被别人删除了】
解决方案:给每个加锁的key生成一个唯一的UUID作为value,删除之前,从redis拿出来的UUID与当前线程一致才能删除key。


然而finally依然存在问题,因为释放锁的代码不是原子操作。【超时零界点】,在判断uuid相同后,且还未删除key前,此时key超时了,其他线程加锁成功,当前线程执行delete命令时,删除的依然是其他线程加的锁。
以上问题均是由于锁的时间不够长导致,接下来的解决方案是【锁续命】
锁续命
原理:线程1加锁成功后,再创建一个线程,使用定时任务来监控锁剩余时间。定时任务执行的周期必须小于锁的超时时间。比如锁超时时间默认设置为30秒,那么该线程每10秒执行一次,给锁重新设置超时时间为30秒, 保证主线程删除锁时,是自己加的锁。如果主线程已释放锁,子线程执行定时任务时会先判断主线程加的key是否还存在。 目前市面上已有成熟的解决方案,如redisson,它适用于分布式各种场景。参考redisson帮助文档
Redisson续命锁流程

引入redisson的依赖包
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.27.2</version>
</dependency>
创建一个Redisson的bean注册到Spring容器中

//获取锁对象
RLock redissonLock = redisson.getLock(lockKey);
//加分布式锁
redissonLock.lock();
........
//解锁
redissonLock.unlock();
Redisson加锁源码
底层是基于lua脚本实现,它能保证原子性。因为Redis服务端执行命令是单线程的,读到这一块lua代码会将它当成一个整体执行完,再去执行下一条命令。
第一步:通过lua脚本加锁成后,返回null,加锁失败返回锁剩余时间。
hset key field value 通过hash结构设置field为UUID+线程ID,value是1,表示重入次数。 key是构建锁时传入的redisson.getLock(lockKey); 锁超时时间默认是30秒。

第二步:看门狗机制给锁续命
Future模式异步执行lua脚本加锁,加锁成功后回调Future的监听器获取加锁结果。 加锁的lua脚本执行成功后返回null,则进入scheduleExpireRenewal()方法定时的给锁重置超时时间。

创建一个TimeTask延时任务,10秒后才执行run()方法,lua脚本判断锁存在则重设锁的超时时间为30秒。它同样是使用的Future模式,下面添加了一个监听,重置结束后,会回调监听器,监听器拿到结果再回调scheduleExpireRenewal()方法本身。


第三步:加锁失败的线程自旋等待
- 加锁失败后,先订阅该key的channel通道消息,类似于一个队列或topic,当锁在过期之前释放了,需要唤醒等待的线程竞争锁。
- 进入while(true)自旋加锁。先尝试一次加锁,成功则退出循环,失败则获取Semaphore调用tryAcquire()等待ttl秒后,重新进入while(true)尝试获取锁。如果1000个线程同时结束等待,就会一起抢锁,所以该锁是非公平锁。


第四步:释放锁时,发布该key的channel消息,通知等待的线程竞争锁。
一旦channel中有消息后,会执行LockPubSub.onMessage()方法,获取一个Semaphora信号量释放等待的线程,让他们竞争锁。


总结:redisson的架构设计涉及到Future,自旋锁,看门狗机制,发布订阅,lua脚本,semaphore等技术。
它的lock()接口实现的是非公平锁。tryAcquire()加锁成功后,继续执行业务代码。其他线程拿锁时返回锁剩余的时间ttl,判断ttl大于0,则使用Semaphore.getLatch().tryAcquire(),等到超时时间结束后再去抢锁。如果ttl还没有变为0,此时锁已释放,主线程会向指定的channel发送一条消息,等待的线程会订阅这个消息LockPubSub.onMessage(),拿到释放锁的消息后调用Semaphore.getLatch().release(),唤醒阻塞的线程立刻抢锁。
主线程给锁续命使用的是Future机制,异步开启了一个线程,每隔10秒嵌套调用重设锁时间的方法。
lua脚本


相关文章:
Redis实现分布式锁源码分析
为什么使用分布式锁 单机环境并发时,使用synchronized或lock接口可以保证线程安全,但它们是jvm层面的锁,分布式环境并发时,100个并发的线程可能来自10个服务节点,那就是跨jvm了。 简单分布式锁实现 SETNX 格式&…...
SCI 图像处理期刊
引用 一区 1. IEEE TRANSACTIONS ON PATTERN ANALYSIS AND MACHINE INTELLIGENCE 顶刊:是 出版商:IEEE 2. IEEE Transactions on Multimedia 顶刊:是 出版商:IEEE 3. Information Fusion 顶刊:是 出版商:ELSEVIER 4.IEEE TRANSACTIONS ON IMAGE PROCESSING 顶刊:是 出版商:I…...
数据结构-红黑树
1.容器 容器用于容纳元素集合,并对元素集合进行管理和维护. 传统意义上的管理和维护就是:增,删,改,查. 我们分析每种类型容器时,主要分析其增,删,改ÿ…...
双指针、bfs与图论
1238. 日志统计 - AcWing题库 import java.util.*;class PII implements Comparable<PII>{int x, y;public PII(int x, int y){this.x x;this.y y;}public int compareTo(PII o){return Integer.compare(x, o.x);} }public class Main{static int N 100010, D, K;st…...
RabbitMQ高级-高级特性
1.消息可靠性传递 在使用RabbitMQ的时候,作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ为我们提供了两种方式来控制消息的投递可靠性模式 1.confirm 确认模式 确认模式是由exchange决定的 2.return 退回模式 回退模式是由routing…...
Word粘贴时出现“运行时错误53,文件未找到:MathPage.WLL“的解决方案
在安装完MathType后,打开word复制粘贴时报错“运行时错误53,文件未找到:MathPage.WLL” 首先确定自己电脑的位数(这里默认32位) 右击MathType桌面图标,点击“打开文件所在位置”, 然后分别找到MathPage.W…...
html元素基本使用
前言 大家好,我是jiantaoyab,第一次学习前端的html,写一篇笔记总结常用的元素 语义化 例如只要是 不管字体的大小是怎么样,有没有加粗都是标题,元素显示到页面中的效果应该由css决定,这就是语义化。 文…...
PHP+golang开源办公系统CRM管理系统
基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约,易于功能扩展,方便二次开发。 服务器运行环境要求 PHP > 7.…...
smartmontools-5.43交叉编译Smartctl
嵌入式系统的sata盘经常故障,需要使用smatctl工具监控和诊断sata故障。 1. 从网上下载开源smartmontools-5.43包。 2. 修改makefile进行交叉编译。 由于软件包中已经包含Makefile.am,Makefile.in。直接运行 automake --add-missing 生成Makefile。 3.…...
idea找不到或无法加载主类
前言 今天在运行项目的时候突然出了这样一个错误:IDEA 错误 找不到或无法加载主类,相信只要是用过IDEA的朋友都 遇到过它吧,但是每次遇到都是一顿焦头烂额、抓耳挠腮、急赤白咧!咋整呢?听我给你吹~ 瞧我这张嘴~ 问题报错 找不…...
2.二进制的方式读写文件
文章目录 写入文件代码运行结果 读出文件代码运行结果 文件打开模式标记(查表) 写入文件 ------写文件一共五步:------ 第一步:包含头文件 第二步:创建流对象 第三步:指定方式打开文件 第四步:…...
Seata的详细解释
什么是seata Seata(Simple Extensible Autonomous Transaction Architecture)是一个开源的分布式事务解决方案。它是由阿里巴巴集团开发的,旨在解决分布式系统中的事务一致性问题。 Seata提供了一种简单易用的方式来实现跨多个数据库和服务的…...
JS手写实现洋葱圈模型
解释洋葱圈模型: 当我们执行第一个中间件时,首先输出1,然后调用next(),那么此时它会等第二个中间件执行完毕才会继续执行第一个中间件。然后执行第二个中间件,输出3,调用next(),执行第三中间件…...
3.Windows下安装MongoDB和Compass教程
Windows下安装MongoDB 总体体验下来,,要比MySQL的安装简单了许多,没有过多的配置,直接就上手了! 1、下载 进入官方的下载页面https://www.mongodb.com/try/download/community,如下选择,我选…...
go反射实战
文章目录 demo1 数据类型判断demo2 打印任意类型数据 demo1 数据类型判断 使用reflect.TypeOf()方法打印go中数据类型,可参考go官方API文档;使用格式化参数%T也能打印数据类型。 package mainimport "fmt" import "reflect" import "io&…...
Docker 中 MySQL 的部署与管理
目录 一、Docker 中部署 MySQL1.1 部署 MySQL1.2 进入容器并创建数据库1.3 Navicat 可视化工具连接 二、可能存在的问题2.1 1130 - Host ‘172.17.0.1‘ is not allowed to connect to this MySQL server 参考资料 一、Docker 中部署 MySQL 1.1 部署 MySQL 首先,从…...
基础练习题之函数
前言 这些题目来自与一些刷题网站,以及c primer plus,继续练习 第一题 给你一个数,让他进行巴啦啦能量,沙鲁沙鲁,小魔仙大变身,如果进行变身的数不满足条件的话,就继续让他变身。。。直到满足条件为止。 巴啦啦能量…...
Java NIO浅析
NIO(Non-blocking I/O,在Java领域,也称为New I/O),是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题…...
数据挖掘与大数据的结合
随着大数据技术的不断发展和普及,数据挖掘在大数据环境下的应用也变得更加广泛和深入。以下将探讨大数据技术对数据挖掘的影响,以及如何利用大数据技术处理海量数据并进行有效的数据挖掘,同时分析大数据环境下的数据挖掘挑战和解决方案。 1.…...
分布式链路追踪(一)SkyWalking(2)使用
一、使用方法 1、简介 agent探针可以让我们不修改代码的情况下,对Java应用上使用到的组件进行动态监控,获取运行数据发送到OAP上进行统计和存储。agent探针在Java使用中是使用Java agent技术实现。不需要更改任何代码,Java agent会通过虚拟…...
Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...
使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台
🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
MySQL 8.0 事务全面讲解
以下是一个结合两次回答的 MySQL 8.0 事务全面讲解,涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容,并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念(ACID) 事务是…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
未授权访问事件频发,我们应当如何应对?
在当下,数据已成为企业和组织的核心资产,是推动业务发展、决策制定以及创新的关键驱动力。然而,未授权访问这一隐匿的安全威胁,正如同高悬的达摩克利斯之剑,时刻威胁着数据的安全,一旦触发,便可…...
数据挖掘是什么?数据挖掘技术有哪些?
目录 一、数据挖掘是什么 二、常见的数据挖掘技术 1. 关联规则挖掘 2. 分类算法 3. 聚类分析 4. 回归分析 三、数据挖掘的应用领域 1. 商业领域 2. 医疗领域 3. 金融领域 4. 其他领域 四、数据挖掘面临的挑战和未来趋势 1. 面临的挑战 2. 未来趋势 五、总结 数据…...
