浅析锁的应用与场景

news2025/4/27 2:58:54

锁的应用与场景:从单机到分布式

摘要:在多线程和分布式系统中,“锁”是避免资源竞争、保障数据一致性的核心机制。但你真的了解锁吗?什么时候该用锁?用哪种锁?本文通过通俗的比喻和代码示例,带你彻底搞懂锁的应用场景!


一、为什么需要锁?

想象一下:多人同时编辑同一份文档,如果不加控制,最终文档内容会乱成一锅粥。程序中的共享资源(如数据库字段、文件、内存变量)同样面临这个问题——锁的作用就是让多个线程/进程“排队”访问资源

常见问题场景:

  1. 订单重复处理:用户疯狂点击提交订单,导致重复扣款。
  2. 超卖问题:秒杀活动中库存被减到负数。
  3. 数据覆盖:两个线程同时修改用户余额,后者覆盖前者结果。

二、单机环境下的锁

1. 乐观锁 vs 悲观锁

  • 悲观锁:假设一定会发生冲突,先加锁再操作。
    • 应用场景:冲突频繁、临界区代码执行时间长。

    • 实现方式

      • 数据库:SELECT ... FOR UPDATE
      • Java:synchronizedReentrantLock
维度synchronizedReentrantLock
锁管理JVM 自动管理,无需手动释放需手动获取和释放,易忘记导致死锁
灵活性功能简单,仅支持非公平锁支持公平锁、超时、中断、多条件变量
性能JVM 优化后性能接近(低竞争场景更优)高并发场景更灵活(如 tryLock 减少竞争)
调试支持锁信息不易获取(如等待队列长度)提供 isLocked(), getQueueLength() 等方法
适用场景简单同步需求(如单方法内的线程安全)复杂同步逻辑(如多条件协调、精细控制)
  • 乐观锁:假设冲突很少,先操作再检查是否冲突。
    • 应用场景:读多写少、冲突概率低。
    • 实现方式
      • 数据库:版本号(Version字段)+ CAS更新
      • Java:AtomicIntegerStampedLock
代码示例:数据库乐观锁
-- 1. 查询时获取版本号
SELECT stock, version FROM product WHERE id = 1;

-- 2. 更新时校验版本号
UPDATE product SET stock = stock - 1, version = version + 1 
WHERE id = 1 AND version = 1; -- 如果version被修改过,更新失败

2. 读写锁(ReadWriteLock)

  • 核心思想:读操作不互斥,写操作互斥。
  • 应用场景:读多写少,如缓存系统。
  • Java实现ReentrantReadWriteLock
ReadWriteLock rwLock = new ReentrantReadWriteLock();

// 读操作
rwLock.readLock().lock();
try {
    // 读取数据(允许多个线程同时读)
} finally {
    rwLock.readLock().unlock();
}

// 写操作
rwLock.writeLock().lock();
try {
    // 修改数据(独占锁)
} finally {
    rwLock.writeLock().unlock();
}

三、分布式锁

当服务部署在多台机器上时(即使在同一台物理机上的多个容器/Pod),单机锁失效,必须使用分布式锁协调跨进程的资源访问。

1. 常见实现方案

方案核心原理优点缺点
Redis锁SETNX + 过期时间 + Lua脚本删除性能高,实现简单存在锁过期提前释放风险
ZooKeeper创建临时顺序节点,监听前序节点删除可靠性高,自动释放锁性能较低,需要维护ZK集群
数据库锁基于唯一索引或行级锁无需额外组件性能差,高并发易成瓶颈
  • 高并发且允许偶发锁失效:Redis + Redisson。
  • 强一致性需求:ZooKeeper 或 Etcd。
  • 简单场景:数据库(不推荐生产环境高频使用)

2. Redis分布式锁示例(Redisson实现)

// 1. 获取锁对象
RLock lock = redissonClient.getLock("orderLock");

// 2. 尝试加锁(等待10秒,锁自动释放时间30秒)
boolean isLocked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (isLocked) {
    try {
        // 处理业务逻辑
        processOrder();
    } finally {
        lock.unlock();
    }
}

四、如何选择合适的锁?

1. 决策流程图

在这里插入图片描述

2. 黄金原则

  • 能用单机锁就别用分布式锁(复杂度陡增)
  • 锁粒度要小:锁住的范围越小,性能越高
  • 优先考虑无锁设计:如使用线程安全的类(ConcurrentHashMap)、本地线程存储(ThreadLocal

五、避坑指南

  1. 死锁:避免嵌套锁,设置超时时间。
  2. 锁饥饿:公平锁可缓解,但性能会下降。
  3. 锁泄露:确保finally块中释放锁。
  4. 脑裂问题(分布式锁):选择强一致性协调器(如ZooKeeper)。

六、总结

  • 单机多线程:本地锁 + 数据库唯一索引即可满足需求,无需分布式锁。优先选synchronizedReentrantLock
  • 多机/多实例:必须引入分布式锁,同时结合数据库约束保证最终安全。
  • 高并发读:读写锁(ReadWriteLock)是救星。
  • 分布式系统:Redis锁(性能)或ZooKeeper锁(可靠性)二选一。
  • 终极目标:在安全性和性能之间找到平衡!

技术没有银弹,理解场景才能选出最合适的锁!

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

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

相关文章

语音合成之五语音合成中的“一对多”问题主流模型解决方案分析

语音合成中的“一对多”问题主流模型解决方案分析 引言“一对多”指的是什么?优秀开源模型的方法CosyvoiceSparkTTSLlaSA TTSVITS 引言 TTS系统旨在模仿人类的自然语音,但其核心面临着一个固有的挑战,即“一对多”问题 。这意味着对于给定的…

ElementUi的Dropdown下拉菜单的详细介绍及使用

Dropdown是 ElementUI 中用于创建下拉菜单项的一个组件,通常el-dropdown-item 包裹在 el-dropdown 组件中使用。以下从功能特性(一些属性及方法)、使用和高级功能(高亮显示,滚动,额外传参数)三个方面进行详细介绍。 一、功能特性 1.触发方式…

Linux麒麟 V10 系统找回 root 密码的步骤

Linux麒麟 V10 系统找回 root 密码的步骤 1 环境介绍2 操作步骤2.1重启系统并进入 GRUB 菜单2.2 输入 GRUB 账户密码2.3 修改启动参数2.4 启动系统2.5 修改root 密码2.6 重启系统 3 Linux命令全方位指南实战教程Linux命令学习使用列表 1 环境介绍 有时候root 密码忘记&#xf…

stone 3d v3.3.0版本发布,含时间线和连接器等新功能

1.新加了时间线(timeline)编辑器,可以类似blender一样给对象制作动画 2.新加了度量(metrics)系统,通过scene对象检测器中的useMetrics属性来启用或禁用,启用时所选物体将显示三维度量数据 新加了…

.whl文件

本文主要介绍了.whl文件的定义,怎么安装.whl文件(离线,在线)。 怎么查看cuda的版本,以及如何安装相应版本的cuda(本地电脑,超算上) 以及如何创建.whl文件 .whl文件的定义 Document…

Git命令行中vim的操作

Git命令行用vim打开文件,或者用其他git命令打开了文件,需要编辑和保存文件等,有些命令表情奇怪,往往容易忘记这些命令。记录下。 下面这篇比较实用和简练: gitvim编辑文件命令 • Worktile社区https://worktile.com/…

C#初级知识总结

一、什么是CIL 1.CIL(Common Intermidate Language)是指.Net的公共中间语言,它是一种编程语言。 .Net框架的各种语言在编译时都会编译成同一种中间语言(CIL),之后程序运行的时候CIL会被JIT(Just In Time)转换为二进制语言&#xf…

Linux学习笔记之环境变量

写这篇博客的目的主要是因为本人学习动静态库时,用到了环境变量的知识,发现略有遗忘,因此回顾复习,整理成博客。 一、环境变量是什么 Linux环境变量是存储系统或程序运行时配置信息的特殊变量,用于为程序提供配置参数…

16:00开始面试,16:08就出来了,问的问题有点变态。。。

从小厂出来,没想到在另一家公司又寄了。 到这家公司开始上班,加班是每天必不可少的,看在钱给的比较多的份上,就不太计较了。没想到4月一纸通知,所有人不准加班,加班费不仅没有了,薪资还要降40%…

私钥连接服务器(已经有服务器私钥

前言:假设我们已经有了服务器的私钥,我们怎么配置呢? 下面我会从vsc的配置角度来写 ✅ 步骤一:准备工作 安装 VS Code(如果还没装) 👉 https://code.visualstudio.com/ 安装插件:Re…

学员答题pk知识竞赛小程序怎么做

制作学员答题PK知识竞赛小程序,主要有以下步骤: 一、规划设计 明确需求:确定小程序的使用场景是校园知识竞赛、培训机构考核还是企业内部培训等。答题功能,规定答题的具体规则,包括题目类型(单选、多选、…

外观模式:简化复杂系统接口的设计模式

外观模式:简化复杂系统接口的设计模式 一、模式核心:为复杂子系统提供统一简单接口 当一个系统由多个复杂子系统组成时(如电商系统中的支付、物流、库存模块),客户端直接调用子系统会导致依赖关系复杂、代码难以维护…

uniapp-商城-36-shop 购物车 选好了 进行订单确认2 支付方式颜色变化和颜色滤镜filter

颜色滤镜&#xff0c;在好多网页都这样使用&#xff0c;滤掉彩色&#xff0c;显示黑白&#xff0c;这在一些关键的日子中都这样使用。 1、依然回到订单确认页面 看到支付的颜色了嘛&#xff1f; <view class"payType"><view class"box" :class&q…

Vue3 上传后的文件智能预览(实战体会)

目录 前言1. Demo12. Demo2 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器&#xff0c;无代码爬取&#xff0c;就来&#xff1a;bright.cn 此处的基本知识涉及较少&#xff0c;主要以Demo的形式供大…

CCE13.【C++ Cont】练习题组13 静态链表专题

目录 1.B3630 排队顺序 题目 分析 代码 提交结果 2.B3631 单向链表 题目 分析 前置知识:map数组加快访问速度(简单的哈希表优化) 使用map数组的重要提醒 代码 提交结果 3.★P1160 队列安排 题目 分析 方法1:带头不循环双向链表的设计 方法2:带头循环的双向链表…

内联函数(c++)

预处理&#xff1a;优点&#xff1a;内嵌到目标代码&#xff0c;减少函数的调用。 缺点&#xff1a;在预处理阶段完成替换&#xff0c;避免了语义上的差错。 egg&#xff1a; #define SQR(X) ((X)*(X)) 函数&#xff1a;优点&#xff1a;完成了某一类操作的抽象&#xff0c;…

R7周:糖尿病预测模型优化探索

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 一、数据预处理 1.设置GPU import torch.nn.functional as F import torch.nn as nn import torch, torchvisiondevice torch.device("cuda"…

线程怎么创建?Java 四种方式一网打尽

&#x1f680; Java 中线程的 4 种创建方式详解 创建方式实现方式是否推荐场景说明1. 继承 Thread 类class MyThread extends Thread❌ 不推荐简单学习、单线程场景2. 实现 Runnable 接口class MyRunnable implements Runnable✅ 推荐更适合多线程共享资源3. 实现 Callable 接…

STM32之DHT11温湿度传感器---附代码

DHT11简介 DHT11的供电电压为 3&#xff0d;5.5V。 传感器上电后&#xff0c;要等待 1s 以越过不稳定状态在此期间无需发送任何指令。 电源引脚&#xff08;VDD&#xff0c;GND&#xff09;之间可增加一个100nF 的电容&#xff0c;用以去耦滤波。 DATA 用于微处理器与DHT11之间…

工业相机——镜头篇【机器视觉,图像采集系统,成像原理,光学系统,成像光路,镜头光圈,镜头景深,远心镜头,分辨率,MTF曲线,焦距计算 ,子午弧矢】

文章目录 1 机器视觉&#xff0c;图像采集系统2 相机镜头&#xff0c;属于一种光学系统3 常规镜头 成像光路4 镜头光圈5 镜头的景深6 远心镜头 及 成像原理7 远心镜头种类 及 应用场景8 镜头分辨率10 镜头的对比度11 镜头的MTF曲线12 镜头的焦距 计算13 子午弧矢 图解 反差 工业…