这是一道架构师 / 高级开发必考题 :把 **Java 锁(线程间)和 数据库锁(事务间)** 放在一起对比,本质是:JVM 内多线程并发控制 vs 多进程 / 多服务间分布式并发控制
我给你做最透彻、最体系化、可直接面试讲的对比详解。
一、核心定位区别(一句话抓住本质)
| 维度 | Java 锁(synchronized、ReentrantLock) | 数据库锁(MySQL InnoDB 为主) |
|---|---|---|
| 作用范围 | JVM 内部,同一进程内,多线程之间 | 数据库实例,跨进程、跨服务、跨机器 |
| 控制目标 | 线程对 内存变量、代码块 的竞争 | 事务对数据行 / 表 的修改 竞争 |
| 解决问题 | 线程安全、共享变量安全 | 事务隔离、数据一致性、防止丢失更新 |
| 并发粒度 | 代码块、对象、变量 | 表、页、行(InnoDB 行锁) |
| 生命周期 | 线程加锁→解锁 | 事务 begin → commit/rollback |
| 底层依赖 | CAS、AQS、Monitor、操作系统 Mutex | 事务日志、undo/redo、MVCC、表 / 行锁结构 |
| 崩溃安全性 | 进程挂了就没了,不保证数据持久安全 | 事务 ACID,崩溃可恢复 |
一句话总结: Java 锁管**"内存"** ,DB 锁管**"数据** ";Java 锁是**"单机线程"** ,DB 锁是**"分布式并发"**。
二、分类体系完全对应(超级好记)
Java 锁的分类,在数据库里几乎都能找到双胞胎。
1. 乐观锁 vs 悲观锁(最核心对应)
Java 层面
- 悲观锁:synchronized、ReentrantLock(先加锁再操作)
- 乐观锁:AtomicInteger、CAS(无锁,最后比较版本 / 值)
数据库层面
-
悲观锁
SELECT ... FOR UPDATE- 增删改自动加排他锁
-
乐观锁
- 业务实现:
version字段
sqlUPDATE t SET ... , version=version+1 WHERE id=? AND version=? - 业务实现:
对比
- 悲观:假定一定会冲突,先锁再做
- 乐观:假定冲突少,先做后验证
2. 排他锁(X 锁) vs 共享锁(S 锁)
Java
- 排他锁:ReentrantLock、synchronized(独占)
- 共享锁:ReentrantReadWriteLock(读锁共享,写锁排他)
DB(InnoDB)
- 排他锁 X :写锁,
FOR UPDATE、DELETE/UPDATE- 互斥:X 与 X、S 都互斥
- 共享锁 S :读锁,
SELECT ... LOCK IN SHARE MODE- 读读共享,读写互斥
对应关系
- Java ReadLock ≈ DB S 锁
- Java WriteLock ≈ DB X 锁
3. 行锁、表锁、页锁(粒度对比)
Java
只有对象 / 代码块锁 ,没有 "粒度升级" 概念。
DB
- 表锁:锁全表(MyISAM、InnoDB 无索引时)
- 行锁:锁单行(InnoDB 索引有效时)
- 页锁:介于两者之间
粒度越小,并发性越高,但开销越大DB 行锁 ≈ Java 细粒度锁,DB 表锁 ≈ Java 全局大锁(性能极差)
4. 公平锁 vs 非公平锁
Java
ReentrantLock 可以手动指定公平 / 非公平。
DB
基本都是非公平锁数据库不保证先来先服务,只保证互斥与隔离。没有 "公平锁" 概念。
5. 可重入锁
Java
synchronized、ReentrantLock 都是可重入。
DB
InnoDB 行锁也是可重入的同一事务内,多次对同一行加锁,不会死锁自己。
6. 死锁
两者死锁条件完全一样(4 个条件):
- 互斥
- 持有并等待
- 不可剥夺
- 循环等待
Java 死锁
- 线程 1 锁 A 等 B
- 线程 2 锁 B 等 A
DB 死锁
- 事务 1 锁行 1 等行 2
- 事务 2 锁行 2 等行 1
处理差异
- Java:JVM 不自动解决,只能靠代码规范
- DB:有死锁检测机制,InnoDB 会主动牺牲一个事务
三、数据库特有:Java 完全没有的机制
1. MVCC(多版本并发控制)
这是数据库最关键、最强大的东西,Java 没有对应。
- 读不加锁,写不阻塞读
- 依靠 undo log + 事务 ID + readView
- 实现 RC、RR 隔离级别
Java 中没有任何机制能对标 MVCC,Java 只能:
- 加锁(阻塞)
- CAS(重试)
- ThreadLocal(隔离)
无法做到无锁非阻塞快照读。
2. 间隙锁(Gap Lock)+ 临键锁(Next-Key Lock)
RR 隔离级别下,解决幻读锁范围:
- 行锁
- 间隙锁
- 左开右闭区间锁
Java 没有 "范围锁" 这种概念。
3. 事务与锁绑定
DB 锁随事务提交 / 回滚自动释放 Java 锁必须手动 unlock 或代码块结束
4. 锁自动升级
DB:
- 行锁太多 → 升级为表锁Java:
- synchronized 是锁升级(偏向→轻量→重量)不是粒度变大,是成本变高
四、Java 锁 vs DB 锁 适用场景(必背)
什么时候用 Java 锁?
- 单机、单 JVM
- 高并发、低延迟
- 内存数据、本地缓存、计数器、限流
- 无持久化需求
什么时候用 DB 锁?
- 跨服务、跨机器、分布式
- 数据库数据一致性
- 防止超卖、扣减库存、订单状态变更
- ACID 必须保证
典型错误
- 用 Java 锁控制 DB 库存 → 超卖必死
- 用 DB 锁做本地限流 → 大材小用、性能爆炸
五、面试满分总结(可直接背诵)
- Java 锁是 JVM 内线程同步,DB 锁是多事务分布式数据同步
- 两者都有:悲观 / 乐观、排他 / 共享、可重入、死锁
- DB 独有:MVCC、行锁、表锁、间隙锁、事务自动释放、死锁检测
- Java 独有:AQS、CAS、公平锁、偏向锁、轻量级锁
- MVCC 是数据库并发核心,Java 无对应机制
- Java 锁管内存,DB 锁管数据,不能互相替代
其他知识点
- MySQL 锁机制全解(行锁、间隙锁、临键锁、加锁规则)
- 秒杀场景:Java 锁 + DB 锁 + 分布式锁 完整方案
- 图解 MVCC 原理(超级易懂版)