Java并发编程 什么是分布式锁 跟其他的锁有什么区别 底层原理 实战讲解

news2025/3/29 16:29:01

目录

一、分布式锁的定义与核心作用

二、分布式锁与普通锁的核心区别

三、分布式锁的底层原理与实现方式

1. 核心实现原理

2. 主流实现方案对比

3. 关键技术细节

四、典型问题与解决方案

五、总结

六、具体代码实现


一、分布式锁的定义与核心作用

分布式锁是一种在分布式系统中协调多进程/节点对共享资源进行互斥访问的机制。其核心作用是确保同一时间只有一个进程能够操作共享资源,解决分布式环境下的并发冲突问题(如超卖、数据覆盖等)。


二、分布式锁与普通锁的核心区别

对比维度

普通锁(线程/进程锁)

分布式锁

作用范围

单机环境(同一JVM或进程内)

跨机器、跨JVM的分布式环境

数据存储

基于内存(如synchronized、Lock)

基于外部存储(如Redis、ZooKeeper、数据库)

锁失效风险

无网络延迟或节点故障风险

需处理网络分区、节点宕机、时钟同步等问题

典型应用场景

单机多线程资源竞争

分布式服务、微服务集群、数据库流量控制等


三、分布式锁的底层原理与实现方式

1. 核心实现原理
  • 互斥性:通过唯一标识(如Redis的Key、ZooKeeper节点路径)确保同一时间仅有一个客户端持有锁。
  • 超时机制:设置锁的过期时间,避免死锁(如Redis的PX参数)。
  • 原子性操作:加锁、解锁需通过原子命令(如Redis的SETNX+EXPIRE组合或Lua脚本)实现。
2. 主流实现方案对比

实现方式

原理

优点

缺点

Redis

基于SET key value NX PX

命令,结合唯一值(UUID)和Lua脚本保证原子性 。

高性能、易扩展

主从切换可能导致锁失效(需RedLock或Redisson优化)

ZooKeeper

基于临时有序节点

,最小序号节点获得锁,通过Watcher监听节点变化 。

强一致性、自动释放锁(节点断开则删除临时节点)

性能较低、实现复杂

数据库

通过唯一约束(如MySQL行锁、乐观锁)或专用锁表 。

简单易用

性能差、高并发场景易成瓶颈

3. 关键技术细节
  • 锁续期(看门狗机制):Redisson通过后台线程定期检查并延长锁有效期,避免业务未完成时锁过期。
  • 可重入性:通过记录线程标识和重入次数(如Redis的Hash结构)支持同一线程多次加锁。
  • 容错设计
    • Redis的RedLock算法需半数以上节点加锁成功,避免主从切换问题。
    • ZooKeeper通过临时节点自动清理解决进程宕机导致的死锁。

四、典型问题与解决方案

  1. 锁过期但业务未完成
    • 方案:使用守护线程续期(如Redisson的看门狗)或超时回滚+告警。
  1. 锁误删(非持有者释放锁)
    • 方案:解锁时校验唯一标识(如UUID),并通过Lua脚本保证原子性。

五、总结

分布式锁通过外部存储系统实现跨进程资源互斥,需权衡性能、一致性和复杂度。Redis适合高频低一致性要求的场景,ZooKeeper适用于强一致性但低并发场景,而数据库锁仅作为简单场景的备选。实际选型需结合业务需求和容错能力(如Redisson整合Redis的方案较优)。

六、具体代码实现

我们这边是查询数据

首先如果缓存命中 就直接返回数据

否则是要去数据库查询数据

使用分布式锁 让同一时间只能允许一个线程更新缓存

防止碰巧有写入缓存的线程结束

我们可以进行一个二次检查 防止那个碰巧情况

因为缓存一旦存在 再次写入 数据会进行叠加

确认了在分布式锁内 缓存依旧为空

之后我们就可以去数据库查询数据

   @Override
    // 这边我们使用redis来辅助mysql查询 因为数据库压力实在是太大了(服务器带宽太低)
    public List<GetAllContentResp> getAll() {
        // 异常处理
        try {
            // 1. 构建带业务标识的复合Key
            String cacheKey = "balloonSentences:all" + DATA_VERSION;
            // 2. 带熔断的缓存读取 如果缓存击中 直接返回即可 返回的是所有数据
            List<GetAllContentResp> cachedData = redisService.getList(cacheKey, 0, -1);
            if (cachedData != null) {
                if (cachedData.isEmpty()) { // 空值缓存处理
                    return Collections.emptyList();
                }
                elasticsearchService.saveProduct(cachedData);  // 写到elasticsearch里面去
                return cachedData;
            } else {
                // 3. 分布式锁防穿透 同一时间只允许一个线程更新缓存
                RLock lock = redissonClient.getLock("lock:" + cacheKey);
                try {
                    lock.lock(5, TimeUnit.SECONDS);
                    // 二次检查
                    cachedData = redisService.getList(cacheKey, 0, -1);
                    if (cachedData != null) return cachedData;
                    // 4. 数据库查询
                    List<GetAllContentResp> dbData = tSentencesMapper.getAll();
                    // 5. 异步写缓存和elasticsearch(保证数据库操作成功)
                    CompletableFuture.runAsync(() -> {
                        // 随机化TTL防雪崩
                        redisService.setList(cacheKey, dbData, RandomUtil.randomInt(30, 60), TimeUnit.MINUTES);
                        elasticsearchService.saveProduct(dbData);  // 写到elasticsearch里面去
                    });
                    return dbData;
                } finally {
                    lock.unlock();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Axure项目实战:智慧城市APP(六)市民互动(动态面板、显示与隐藏)

亲爱的小伙伴&#xff0c;在您浏览之前&#xff0c;烦请关注一下&#xff0c;在此深表感谢&#xff01; 课程主题&#xff1a;市民互动 主要内容&#xff1a;动态面板、显示与隐藏交互应用 应用场景&#xff1a;AI产品交互、互动类应用 案例展示&#xff1a; 案例视频&am…

为何服务器监听异常?

报错&#xff1a; 执行./RCF后出现监听异常--在切换网络后&#xff0c;由于前面没有退出./RCF执行状态&#xff1b;重新连接后&#xff0c;会出现服务器监听异常 原因如下&#xff1a; 由于刚开始登录内网&#xff0c;切换之后再重新登录内网&#xff0c;并且切换网络的过程中…

1.认识Excel

一 Excel 可以用来做什么 二 提升技巧 1.数据太多 2.计算太累 3.提升数据的价值和意义 4.团队协作 三 学习目标 学习目标不是为了掌握所有的技能&#xff0c;追逐新功能。而是学知识来解决需求&#xff0c;如果之前的技能和新出的技能都可以解决问题&#xff0c;那不学新技能也…

光谱范围与颜色感知的关系

光谱范围与颜色感知是光学、生理学及技术应用交叉的核心课题&#xff0c;两者通过波长分布、人眼响应及技术处理共同决定人类对色彩的认知。以下是其关系的系统解析&#xff1a; ‌1.基础原理&#xff1a;光谱范围与可见光‌ ‌光谱范围定义‌&#xff1a; 电磁波谱中能被特定…

网络地址转换技术(2)

NAT的配置方法&#xff1a; &#xff08;一&#xff09;静态NAT的配置方法 进入接口视图配置NAT转换规则 Nat static global 公网地址 inside 私网地址 内网终端PC2&#xff08;192.168.20.2/24&#xff09;与公网路由器AR1的G0/0/1&#xff08;11.22.33.1/24&#xff09;做…

Python正则表达式(一)

目录 一、正则表达式的基本概念 1、基本概念 2、正则表达式的特殊字符 二、范围符号和量词 1、范围符号 2、匹配汉字 3、量词 三、正则表达式函数 1、使用正则表达式&#xff1a; 2、re.match()函数 3、re.search()函数 4、findall()函数 5、re.finditer()函数 6…

【TI MSPM0】PWM学习

一、样例展示 #include "ti_msp_dl_config.h"int main(void) {SYSCFG_DL_init();DL_TimerG_startCounter(PWM_0_INST);while (1) {__WFI();} } TimerG0输出一对边缘对齐的PWM信号 TimerG0会输出一对62.5Hz的边缘对齐的PWM信号在PA12和PA13引脚上&#xff0c;PA12被…

MySQL: 创建两个关联的表,用联表sql创建一个新表

MySQL: 创建两个关联的表 建表思路 USERS 表&#xff1a;包含用户的基本信息&#xff0c;像 ID、NAME、EMAIL 等。v_card 表&#xff1a;存有虚拟卡的相关信息&#xff0c;如 type 和 amount。关联字段&#xff1a;USERS 表的 V_CARD 字段和 v_card 表的 v_card 字段用于建立…

更改 vscode ! + table 默认生成的 html 初始化模板

vscode ! 快速成的 html 代码默认为&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>D…

使用LVS的 NAT 模式实现 3 台RS的轮询访问

节点规划 1、配置RS RS的网络配置为NAT模式&#xff0c;三台RS的网关配置为192.168.10.8 1.1配置RS1 1.1.1修改主机名和IP地址 [rootlocalhost ~]# hostnamectl hostname rs1 [rootlocalhost ~]# nmcli c modify ens160 ipv4.method manual ipv4.addresses 192.168.10.7/24…

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…

华为p10 plus 鸿蒙2.0降级emui9.1.0.228

需要用到的工具 HiSuite Proxy V3 华为手机助手11.0.0.530_ove或者11.0.0.630_ove应该都可以。 官方的通道已关闭&#xff0c;所以要用代理&#xff0c;127.0.0.1端口7777 https://www.firmfinder.ml/ https://professorjtj.github.io/v2/ https://hisubway.online/articl…

C# Modbus RTU学习记录

继C# Modbus TCP/IP学习记录后&#xff0c;尝试串口通信。 操作步骤&#xff1a; 1.使用Visual Studio安装Nuget包NModbus.Serial。 2.使用Modbus Slave应用程序&#xff0c;工具栏Connection项&#xff0c;单击Connect&#xff0c;弹窗Connection Setup&#xff0c;修改Con…

AI+Xmind自动生成测试用例(思维导图格式)

一、操作步骤: 步骤1:创建自动生成测试用例智能体 方式:使用通义千问/豆包智能体生成,以下两个是我已经训练好的智能体,直接打开使用即可 通义智能体: https://lxblog.com/qianwen/share?shareId=b0cd664d-5001-42f0-b494-adc98934aba5&type=agentCard 豆包智能…

(二)手眼标定——概述+原理+常用方法汇总+代码实战(C++)

一、手眼标定简述 手眼标定的目的&#xff1a;让机械臂和相机关联&#xff0c;相机充当机械臂的”眼睛“&#xff0c;最终实现指哪打哪 相机的使用前提首先需要进行相机标定&#xff0c;可以参考博文&#xff1a;&#xff08;一&#xff09;相机标定——四大坐标系的介绍、对…

【Linux网络-NAT、代理服务、内网穿透】

一、NAT技术 1.NAT技术背景 之前我们讨论了&#xff0c;IPV4协议中&#xff0c;IP地址数量不充足的问题 NAT技术当前解决IP地址不够用的主要手段&#xff0c;是路由器的一个重要功能 NAT&#xff08;网络地址转换&#xff0c;Network Address Translation&#xff09;是一种…

新手村:逻辑回归-理解02:逻辑回归中的伯努利分布

新手村&#xff1a;逻辑回归-理解02&#xff1a;逻辑回归中的伯努利分布 伯努利分布在逻辑回归中的潜在含义及其与后续推导的因果关系 1. 伯努利分布作为逻辑回归的理论基础 ⭐️ 逻辑回归的核心目标是: 建模二分类问题中 目标变量 y y y 的概率分布。 伯努利分布&#xff08…

golang Error的一些坑

golang Error的一些坑 golang error的设计可能是被人吐槽最多的golang设计了。 最经典的err!nil只影响代码风格设计&#xff0c;而有一些坑会导致我们的程序发生一些与我们预期不符的问题&#xff0c;开发过程中需要注意。 ​​ errors.Is​判断error是否Wrap不符合预期 ​…

【STM32】知识点介绍二:GPIO引脚介绍

文章目录 一、概述二、GPIO的工作模式三、寄存器编程 一、概述 GPIO&#xff08;英语&#xff1a;General-purpose input/output&#xff09;,即通用I/O(输入/输出)端口&#xff0c;是STM32可控制的引脚。STM32芯片的GPIO引脚与外部设备连接起来&#xff0c;可实现与外部通讯、…

【AI】NLP

不定期更新&#xff0c;建议关注收藏点赞。 目录 transformer大语言模型Google Gemma疫情网民情绪识别 整体框架 baseline构建 模型调参、模型优化、其他模型 数据trick、指标优化、magic feature 数据增强、伪标签、迁移学习 模型融合sklearn中TFIDF参数详解 频率阈值可以去掉…