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

并发编程中的乐观锁与悲观锁

一、核心思想:不同的并发哲学

乐观锁和悲观锁是处理数据竞争(多个线程可能同时修改同一数据)的两种不同策略。它们的区别源于对“冲突发生概率”的不同假设。

二、对比总结先行

特性 悲观锁 乐观锁
哲学 假设冲突很可能发生 假设冲突不太可能发生
机制 先取锁,再操作 先操作,更新前再检查冲突
实现 synchronized, ReentrantLock, FOR UPDATE CAS、版本号、原子变量
线程状态 其他线程会**被阻塞(挂起) ** 其他线程不被阻塞,可继续执行,但可能需要重试
开销 (加锁、解锁、上下文切换) (无锁操作,但冲突时重试有开销)
适用场景 写操作多,冲突频繁的场景 读操作多,冲突稀少的场景

三、悲观锁

1. 定义

悲观锁假定冲突非常可能发生。因此,它在访问共享数据之前,会先独占性地锁定资源,阻止其他任何线程访问,直到它完成操作并释放锁。

2. 工作流程

  1. 获取锁:线程A在读取数据前,先成功获取到资源的锁。
  2. 阻塞他人:线程B也想访问该资源,但发现锁已被A持有,于是线程B被挂起(进入阻塞状态),等待锁被释放。
  3. 操作数据:线程A可以安心地读取数据、进行计算、修改数据。这个过程是排他的。
  4. 释放锁:线程A完成操作,释放锁。
  5. 唤醒他人:锁被释放后,系统唤醒正在等待的线程B(和其他线程),它们可以尝试重新获取锁以访问资源。

3. 实现方式

  • synchronized 关键字(Java)
  • ReentrantLock 等显式锁(Java)
  • SELECT ... FOR UPDATE(数据库中的行锁/表锁)

4. 优缺点

  • 优点:简单粗暴,能保证最高的数据安全性和一致性。

  • 缺点:性能开销大。加锁和释放锁的操作本身消耗资源,更重要的是,线程的挂起和唤醒是非常昂贵的操作,会导致上下文切换。如果一个线程持有锁的时间很长,其他所有线程都会被阻塞,严重影响系统吞吐量。

5.适用场景

写多读少的场景,即冲突发生的概率确实很高。在这种情况下,乐观锁会频繁失败重试,反而可能比悲观锁的直接阻塞性能更差。

四、乐观锁

1. 定义

乐观锁假定冲突不太可能发生。因此,它不会在访问数据时立即加锁,而是先直接操作数据。但在更新数据的那一刻,它会检查在此期间是否有其他线程修改过这个数据。如果没有,就更新成功;如果有,就更新失败,并进行相应的处理(通常是重试或报错)。

2. 工作流程

  1. 读取与记录:线程A读取数据,并记录下数据的版本号(Version)或某种校验值(如CAS中的旧值)。
  2. 操作数据:线程A在本地进行计算修改(这一步不加锁,所以其他线程可能已经修改了数据)。
  3. 写前验证:线程A准备将更新写回时,会检查当前的版本号是否和它最初读取的版本号一致。
    • 如果一致:说明没有其他线程修改过数据,于是执行更新操作,并通常会增加版本号。
    • 如果不一致:说明数据已被其他线程修改,本次更新失败
  4. 失败处理:更新失败后,常见的策略是重试(重新读取最新数据和版本号,然后再次执行整个计算和更新流程)。

3. 实现方式

  • CAS指令:CPU级别的原子操作(Compare-And-Swap)。它是乐观锁技术的底层基石。

    • 操作:CompareAndSet(oldValue, newValue)。只有在当前值等于 oldValue 时,才会将其设置为 newValue
  • 原子类:Java中的 AtomicIntegerAtomicLongAtomicReference 等就是基于CAS实现的。

  • 版本号机制:数据库和乐观锁框架(如Hibernate)中常用。在数据表中增加一个 version 字段。

4. 优缺点

  • 优点:性能高。在没有冲突的情况下,它完全避免了加锁、解锁、线程阻塞和上下文切换的开销,吞吐量极高。

  • 缺点:

    • ABA问题:一个值原来是A,被另一个线程改为B,后又改回A。使用CAS的线程会误以为它没变过。通常通过附加版本号或时间戳来解决。
    • 循环时间长开销大:如果冲突频繁,重试操作会持续进行,可能CPU开销很大。
      *** 只能保证一个共享变量的原子操作**。对多个共享变量操作时,CAS无法保证原子性,可能需要用锁。

5.适用场景

读多写少的场景,即冲突发生的概率很低。这是它的理想舞台,能发挥其无锁化的巨大性能优势。

五、结论

选择哪种锁,取决于你对冲突概率的判断。在高并发的互联网应用中,读远多于写是非常普遍的情况,因此乐观锁(及其变种)的应用更为广泛。

http://www.agseo.cn/news/372/

相关文章:

  • 软件工程第一次作业(aili)
  • 软考高级“系统架构设计师”论文——论微服务架构及其应用
  • 2025-09-08 uniapp小程序赋值生效了但是页面却没变化?==》使用v-if+变量来控制元素的重新渲染
  • 真题补题笔记
  • 12.8 类与对象的绑定方法和非绑定方法
  • Graspnet视觉抓取(一)——环境搭建
  • 3. 堆排序
  • 12.7 类的property/setter/delter特性
  • 9.8
  • 总结
  • 82python解析器反查当前安装了那些依赖包
  • 【Azure Container App】查看当前 Container App Environment 中的 CPU 使用情况的API
  • nfs服务
  • 低功耗蓝牙BLE与小程序通讯
  • 同事突然关心有没有对象?这可能是职场发展的隐形陷阱
  • TTS微软Azure
  • 12.6 类的封装
  • 深度解码你自己看着办:职场新人必须掌握的潜台词破解术
  • 6 个替代 Jira 的开源项目管理工具推荐
  • 记录一个Windows上的键盘鼠标模拟库和沟子库--Input
  • 惊世骇俗:《易经》六十四卦与数学公理完整映射表
  • 解决docker: Error response from daemon: Get “https://registry-1.docker.io/v2/“:连接超时问题
  • 27届春招备战一轮复习--第三期(推荐)
  • 数据集和数据系统_AI成为工作中很好用的协同成员了
  • IDM超详细图文安装激活教程,一次安装免费使用 Internet Download Manager
  • 标题
  • 12.5 多态与多态性
  • 集训日记
  • 数字孪生技术如何破解产线效率瓶颈? - 智慧园区
  • 三期集训 日记?