悲观锁
**悲观锁(Pessimistic Lock)**是一种并发控制策略,它的核心思想是:
“我觉得别人一定会来修改这条数据,所以我在操作它之前,必须先把它锁住。”
简单说:先加锁,再操作,别人不能动。
🚪 一、为什么叫“悲观”?
因为这种锁假设并发冲突很可能发生,所以对每一次数据访问都小心谨慎——只要要读或写,就先加锁,避免别人修改。
🔒 二、悲观锁是如何工作的?
当一个事务准备读取或更新某条记录时:
-
先锁定这条记录
-
其他事务想读或写这条记录时,都会被阻塞(等待锁释放)
-
当前事务完成后释放锁
-
其他事务继续执行
🧱 三、常见的悲观锁方式
✔ 1. 数据库层 - 行级锁(最常见)
例如在 MySQL 的 InnoDB 中:
SELECT * FROM users WHERE id = 1 FOR UPDATE;
FOR UPDATE 会对读取的行加 排他锁(X锁):
-
其他事务不能再读(如果是 RC/READ_COMMITTED)
-
不能写
-
必须等待锁释放
✔ 2. 表级锁 / 排他锁
比如:
LOCK TABLES users WRITE;
整个表被锁住,其他事务不能写。
✔ 3. Java 中的 synchronized / ReentrantLock
也属于悲观锁——线程进入共享资源前先上锁。
⚡ 四、悲观锁优点 VS 缺点
👍 优点
-
并发冲突必然被避免
-
逻辑简单
-
适合“高冲突”和“数据一致性要求极高”的场景
👎 缺点
-
性能差:因为会阻塞其他操作
-
可能导致死锁
-
锁竞争会造成排队等待
🆚 五、悲观锁 vs 乐观锁
| 悲观锁 | 乐观锁 | |
|---|---|---|
| 思想 | 冲突会发生,先锁再说 | 冲突不一定发生,先更新,失败重试 |
| 实现 | 数据库行锁、表锁 | version 字段、CAS |
| 并发 | 低并发性能差 | 高并发性能更好 |
| 适用场景 | 金融、电商库存等高一致性场景 | 读多写少场景(配置、资料更新等) |
🧩 六、使用场景示例
🌰 1:下单扣库存(高争抢)
多个用户争抢库存:
-
悲观锁确保每次只能一个人扣库存 → 一定不会出现超卖
-
乐观锁也行,但可能会不断 retry
🌰 2:修改余额(必须一致)
余额不能出错,因此银行操作常使用悲观锁:
SELECT balance FROM account WHERE id = 100 FOR UPDATE;
UPDATE account SET balance = balance - 100 WHERE id = 100;
✔ 小结
悲观锁:
- “我认为你会修改,所以我先锁住”
- 加锁严格、串行化强
- 数据库行锁/表锁、Java synchronized 都属于悲观锁机制