news 2026/5/9 23:36:49

一文搞懂:共享锁、排它锁、乐观锁、悲观锁——它们到底是什么关系?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一文搞懂:共享锁、排它锁、乐观锁、悲观锁——它们到底是什么关系?

📌 写在前面

在学习Java并发编程和数据库事务的过程中,“锁”这个概念几乎无处不在。共享锁、排它锁、互斥锁、读锁、写锁、乐观锁、悲观锁……光是听到这些名字就已经让人一头雾水了。更让人困惑的是,这些锁的名称还经常互相混用——读锁就是共享锁吗?互斥锁就是排它锁吗?乐观锁和悲观锁跟前面那些锁是什么关系?它们是在Java中存在的,还是在数据库中存在的?还是两者都存在?

要搞清楚这些问题,关键不是死记硬背锁的名字,而是理解一个东西:这些锁是从不同角度来分类的,不要把不同分类标准的锁混在一起比较,那必然乱套。

1️⃣ 锁的分类维度:三个角度看懂所有锁

“锁”这个字写在纸上,但从不同的角度看过去,看到的是完全不同的东西。所以你的困惑“共享锁是不是就是乐观锁”之类的——本质就是把不同角度的锁混在一起做比较了。

锁有三层分类逻辑,彼此是“正交”的,互不重叠:

三层分类的逻辑关系如下:

三个维度之间的关系是:策略层决定用悲观还是乐观的态度 →能力层决定执行时提供共享还是排它的权限 →落地层决定在JVM内存还是数据库里,以什么粒度落地执行。

接下来,针对你关心的“概念混淆”问题,逐一从这三个维度拆解。

2️⃣ 按行为模式分类:乐观锁 vs 悲观锁(策略层)

这两种锁,并不是某种具体的锁,而是处理并发问题的两种“战略方针”

从场景出发:做人的态度

在现实生活中,如果我们要去一个需要排队的窗口办业务(比如银行柜台),我们的应对策略会因为对别人的不信任程度而有所变化:

  • 悲观的人(悲观锁)——“怎么会有人没底线呢?”

    先把窗口占住,然后才办理业务。办理的过程中,把所有后面来的人全挡在外面。这样办起来特别稳,但其他人全部进不来。

  • 乐观的人(乐观锁)

    不占窗口,只是在电脑上提交申请时附上“我的身份证号是xxx、我是第xx号”的凭证。如果在这个瞬间系统发现你的名额被人抢先占用了(版本号变了),就退回你的申请,告诉你“冲突了,回去重办”。办得顺畅时速度很快,但需要不断核对,核对的过程中可能跟别人互飙速度。

明白了这个态度选择,再接着看它们的技术实现规则:

悲观锁(Pessimistic Lock)

【态度】我在做操作的时候,一定会有人来抢,所以我先下手为强,先把锁占住!

在Java中的实现

// synchronized:一行代码直接锁,就是最纯粹的悲观锁 public synchronized void deductStock() { ... } // ReentrantLock:手动lock/unlock,也是悲观锁 Lock lock = new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); }

Java中的synchronizedReentrantLock就是悲观锁。它在JVM层面锁住了某个对象或代码块,一旦有线程进入,其他线程必须阻塞等待。

在MySQL中的实现

BEGIN; -- 手动加排他锁,直接锁住这一行,其他事务别想动 SELECT * FROM product WHERE id = 1 FOR UPDATE; UPDATE product SET stock = stock - 1 WHERE id = 1; COMMIT;

MySQL的SELECT ... FOR UPDATE就是数据库层面的悲观锁。

【到底好用在哪 & 疼在哪?】

  • 优点:非常可靠、强一致,能绝对保证并发修改不会相互覆盖。

  • 缺点:性能差,锁等待时间长,容易引发死锁,不适合高并发场景。

  • 适用场景:写多读少(写操作量大)、数据一致性要求极高(如金融转账、库存扣减)的场景。

乐观锁(Optimistic Lock)

【态度】我觉得不会有人跟我抢,我先干我的活儿,最后确认一下没被人改过就行。

在Java中的实现

// AtomicInteger 基于 CAS,整个过程不阻塞其他线程 AtomicInteger stock = new AtomicInteger(100); stock.compareAndSet(100, 99); // CAS操作:如果当前值是100,就设为99,否则重试

Java中的AtomicIntegerAtomicLong等原子类,本质上是基于CAS(Compare And Swap,比较并交换)实现的乐观锁。它利用CPU硬件指令保证比较和交换的原子性,实现无锁编程。

在MySQL中的实现

-- 给表加一个版本号字段 UPDATE product SET stock = stock - 1, version = version + 1 WHERE id = 1 AND version = 5; -- 如果影响行数为0,说明version已被改过,需要重试

MySQL本身并不提供乐观锁语法,乐观锁是在应用层通过版本号或时间戳手动实现的并发控制策略。也可以用框架实现(JPA中的@Version注解)。

在Spring Data JPA / Hibernate中的实现

@Entity public class Product { @Version private Integer version; // 加了这个注解,自动实现乐观锁 }

Hibernate会在更新时自动检查版本号,如果版本不一致就抛OptimisticLockException

【到底好用在哪 & 疼在哪?】

  • 优点:无锁或轻量锁,性能高,适合读多写少的场景。

  • 缺点:冲突率高时会频繁重试,消耗CPU,ABA问题(值从A变B又变回A,CAS误以为没变过)需要额外处理。

  • 适用场景:读多写少(修改频率低)、数据冲突概率小(如用户资料更新、状态机流转)。

3️⃣ 按权限操作分类:共享锁 vs 排它锁(能力层)

这种分类是告诉你:这个锁的能力有多强?允许多人一起读,还是只能一个人独享?

共享锁(Shared Lock)—— 读锁

规则:多个事务/线程可以同时持有共享锁,大家都只读不写,互不干扰。但一旦有人加了共享锁,就禁止任何排他锁加进来。S锁与S锁兼容,S锁与X锁互斥。兼容矩阵如下:

在Java中的实现

Java中的共享锁代表是ReentrantReadWriteLock的读锁部分:

ReadWriteLock rwLock = new ReentrantReadWriteLock(); Lock readLock = rwLock.readLock(); // 共享锁 Lock writeLock = rwLock.writeLock(); // 排它锁 // 读锁:多个线程可以同时获取 readLock.lock(); try { // 读取共享数据,这里实际上是共享锁,允许多个读线程并发 } finally { readLock.unlock(); }

读写锁的核心设计理念正是基于共享/排它锁的兼容规则——“读读共享,读写互斥,写写互斥”。

在MySQL中的实现

-- 显式加共享锁(MySQL 8.0+) SELECT * FROM product WHERE id = 1 FOR SHARE; -- MySQL 8.0之前的写法 SELECT * FROM product WHERE id = 1 LOCK IN SHARE MODE;

加了共享锁后,其他事务可以继续加共享锁读,但不能加排他锁写,保证了读操作的一致性。

排它锁(Exclusive Lock)—— 写锁 / 互斥锁

规则:排他锁是独占性质的,一个事务/线程持有X锁后,其他所有事务/线程都不能加任何锁(S或X),也无法进行写入操作。简单来说就是“生人勿近,老子全占”。

在Java中的实现

ReentrantReadWriteLock的写锁就是典型的排它锁,此外这个角色几乎也由ReentrantLocksynchronized来扮演。可以说,排它锁是所有Java悲观锁的内核属性。

rwLock.writeLock().lock(); try { // 写操作,此时其他线程既不能读也不能写 } finally { rwLock.writeLock().unlock(); }

但注意,Java中的排它锁不一定非得配共享锁配套出现;在很多场景下(如synchronized修饰的普通方法),我们直接用排他独占模式而不提供“读共享”的路径。

在MySQL中的实现

-- 手动排他锁 SELECT * FROM product WHERE id = 1 FOR UPDATE; -- DML语句会自动加排他锁 UPDATE product SET stock = stock - 1 WHERE id = 1; UPDATE/DELETE/INSERT语句会自动给涉及的行加X锁[reference:11]。 ### 🔓 互斥锁(Mutex)又是什么? 你提到的**互斥锁**,在Java和操作系统领域里可以理解为**排它锁的别名**。它的核心就是“独占”——同一时刻只允许一个线程持有该锁。在Java中,`synchronized`和`ReentrantLock`就是互斥锁的经典实现[reference:12]。 在MySQL中,排它锁就是互斥锁的体现——一个事务加了X锁之后,其他事务无法加任何锁。所以简单粗暴地记住:**互斥锁 = 排它锁的别称**。不是一个全新的东西。只是在并发编程中,“互斥”这个词更强调这种锁的独占行为本身,而排它锁更体现的是它的“能力”。 ## 4️⃣ 按实现粒度分类:表锁、行锁、间隙锁(落地层) 这一层决定的是“在哪个范围内加锁”,锁的颗粒度越大,并发性越低。 ### 4.1 MySQL InnoDB 的锁粒度体系 InnoDB存储引擎支持多层次锁粒度,从粗到细依次为: ```mermaid flowchart LR subgraph Granularity [锁粒度] direction LR A[全局锁<br>整库只读] --> B[表锁<br>锁定整张表] B --> C[行锁<br>锁定单行记录] C --> D[间隙锁<br>锁定索引间隙] D --> E[临键锁<br>记录锁+间隙锁] end

4.2 Java 层面的锁粒度(JVM层)

Java的锁主要作用于JVM内存中的对象

  • 对象锁:锁住一个具体的对象实例(如synchronized(this)

  • 类锁:锁住整个类的Class对象(如synchronized(Xxx.class)),所有实例共享一把锁

  • 代码块锁:锁住一段指定的代码区域,粒度比对象锁更细,可以精细控制

和数据库不同,Java锁没有“行锁”或“页锁”的概念,因为Java锁锁的是代码和内存中的对象,不是数据表的记录。数据库锁锁的是持久化的数据行,而Java锁锁的是运行时内存中的对象。

5️⃣ Java中的锁 vs 数据库中的锁

这也是你的疑惑——“这些锁到底存在在哪里?”答案是他们分属不同的地盘,但思想相通。

一句话总结:Java里的锁管的是代码逻辑,数据库里的锁管的是数据本身。做微服务的时候,你的服务可能是分布式集群,Java自带的锁在这个场景下越狱不出去,需要依赖数据库锁或者分布式锁来保证跨节点的互斥。

6️⃣ 全链路横向对比:一张深度对比表看懂所有锁

每个锁都有自己专属的“历史使命”。不同维度的锁之间,不但不冲突,反而需要互相搭配。

7️⃣ 实战场景:不同场景该用什么锁?

在写代码前先冷静地判断两件事:1)这是Java层锁、数据库锁还是分布式锁? 2)结合数据冲突概率,决定用乐观锁还是悲观锁?

8️⃣ 常见面试题与避坑指南

Q1:共享锁就是乐观锁吗?

不是。共享锁(读锁)是一种锁的能力,描述的是“允许多个事务同时读”。乐观锁是一种锁的策略,描述的是“假设不会冲突,最后用版本号检验”。两者完全不同。

Q2:互斥锁就是排它锁吗?

可以近似理解为是同一个东西。互斥锁的核心就是“独占”——和排它锁的能力完全一致。在Java中,互斥锁一般指synchronizedReentrantLock;在数据库中,互斥锁就是排它锁。

Q3:Java中的synchronized是悲观锁还是乐观锁?

悲观锁。它的态度是“我先锁住,不让别人进来”,这是典型的悲观策略。

Q4:SELECT ... FOR UPDATE加了什么锁?

排它锁(写锁)FOR UPDATE会让InnoDB给命中的行或间隙加排它锁,防止其他事务读或写。

Q5:什么是ABA问题?怎么解决?

CAS乐观锁中,值从A变成B又变回A,CAS看到还是A就认为没变过,但实际上已变过。用AtomicStampedReference或数据库版本号(version字段)解决。

避坑指南

  1. 用Redis分布式锁时记得设置超时时间,防止死锁。

  2. 高并发下尽量用乐观锁,少用悲观锁。悲观锁的行锁会大幅降低系统吞吐量。

  3. MySQL意向锁是自动加的,不需要也不允许手动干涉。

  4. InnoDB行锁是基于索引的,没命中索引会升级为表锁

在一个高并发的秒杀系统中,有人主张用数据库乐观锁+重试机制来防止超卖,有人坚决主张用Redis分布式锁+数据库悲观锁FOR UPDATE。请分析这两种方案的核心矛盾在哪里?若让你设计,你会怎么组合不同层次的锁来达到“既要极速响应,又要数据0误差”的双重目标?(欢迎在评论区分享你的设计方案和踩坑经验)

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/9 23:36:48

长期戴假发,头皮怎么护理?

天天戴假发的朋友&#xff0c;这篇你得多看两眼。第一条铁律&#xff1a;每天让头皮透口气不管多忙&#xff0c;每天至少摘下来一小时。你想想&#xff0c;头皮一整天闷在假发里&#xff0c;汗出不去&#xff0c;油排不掉&#xff0c;时间长了毛囊不出问题才怪。这一小时&#…

作者头像 李华
网站建设 2026/5/9 23:36:35

全国青少年人工智能辅助生成数字艺术创作者大赛

官网 全国青少年人工智能辅助生成数字艺术创作者大赛 http://www.aigcnyacc.com/www/index.html?v0507 文档 https://www.mcasc.com.cn/Uploads/Picture/2026/04/29/u69f1718e43b37.pdf https://www.mcasc.com.cn/Uploads/Picture/2026/04/29/u69f1726b33890.pdf

作者头像 李华
网站建设 2026/5/9 23:35:36

Python简单视频下载指南:通用方法与实现

在数字时代&#xff0c;我们经常需要下载视频用于离线观看或个人学习。本文将介绍如何使用Python编写一个简单的视频下载工具&#xff0c;不涉及任何特定视频网站&#xff0c;而是聚焦于通用技术原理和实现方法。 基本原理 视频下载的核心流程通常包括&#xff1a; 获取视频资源…

作者头像 李华
网站建设 2026/5/9 23:35:34

AI工具搭建自动化视频生成Notion集成

# 用AI搭个自动化的视频生成流水线&#xff1a;ShotGrid入门 花了不少时间折腾自动化视频生成&#xff0c;今天聊聊ShotGrid这东西。不算什么新鲜玩意儿&#xff0c;但最近因为AI工具集成&#xff0c;确实又让人眼前一亮。 它到底是什么 ShotGrid本质上是个项目管理工具&#x…

作者头像 李华
网站建设 2026/5/9 23:32:39

AI系统安全防护:基于贝叶斯风险分析的动态组合优化实践

1. 项目概述与核心问题拆解最近几年&#xff0c;AI系统从实验室走向了生产环境&#xff0c;从推荐算法、自动驾驶到工业质检&#xff0c;几乎无处不在。但随之而来的&#xff0c;是攻击者也开始把目光投向了这些系统。传统的网络安全防护&#xff0c;比如防火墙、入侵检测&…

作者头像 李华
网站建设 2026/5/9 23:30:29

2025届必备的十大AI写作神器实测分析

Ai论文网站排名&#xff08;开题报告、文献综述、降aigc率、降重综合对比&#xff09; TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 将人工智能生成内容的比例予以降低&#xff0c;这要从语言特征以及逻辑结构方面切入。其一&a…

作者头像 李华