
一、悲观锁( Pessimistic Lock )
定义与核心思想
悲观锁基于"数据并发必然冲突"的假设,在访问共享资源前强制加锁,确保独占访问。其核心是"先加锁,后操作"的保守策略。
实现方式
1. Java层面
-
synchronized关键字(重量级锁实现)
-
ReentrantLock等显式锁
2. 数据库层面
-
SELECT FOR UPDATE(行级/表级锁)
-
共享锁(S锁)、排他锁(X锁)
特点与优劣势
优势:
-
安全性极高,严格保证数据一致性
-
适合临界区执行时间长的场景
劣势:
1.锁开销大(用户态/内核态切换)
2.可能引起线程阻塞、优先级反转
3.死锁风险
二、乐观锁(Optimistic Lock)
定义与核心思想
乐观锁基于"数据冲突概率低"的假设,采用"先操作,后验证"的开放策略,通过版本控制实现无锁化并发。
实现方式
1. 版本号机制
数据表增加version字段,更新时校验版本[4][15][18]
2. CAS (Compare And Swap )
-
原子操作包含三个参数:内存值V、预期值A、新值B
-
仅在V==A时更新为B,否则重试
-
Java实现:AtomicInteger等原子类
特点与优劣势
优势:
-
无锁设计提升吞吐量
-
避免线程上下文切换
-
适合读多写少场景
劣势:
-
ABA问题(需版本号辅助解决)
-
高竞争场景导致频繁重试
-
需业务层处理冲突
三、关键差异对比
维度 | 悲观锁 | 乐观锁 |
---|---|---|
并发假设 | 必定冲突 | 大概率无冲突 |
锁机制 | 显式加锁 | 无锁(版本控制) |
冲突处理 | 预防冲突 | 检测并解决冲突 |
适用场景 | 写操作多、临界区耗时长 | 读操作多、临界区执行快 |
实现复杂度 | 简单(JVM/DB原生支持) | 需业务层处理冲突逻辑 |
典型应用 | 银行转账、库存扣减 | 点赞计数、配置更新 |
性能表现 | 高安全但吞吐量低 | 高吞吐但存在重试开销 |
四、架构选型建议
-
优先考虑乐观锁:
-
当系统读占比超过70%
-
业务能容忍短暂数据不一致(如缓存更新)
-
需支持高并发(如电商秒杀库存校验)
-
-
必须使用悲观锁:
-
强事务一致性要求(如金融交易)
-
临界区包含复杂计算(避免重复执行)
-
无法处理重试的场景(如实时竞价系统)
-
-
混合使用策略:
- 例如JUC中的StampedLock,先尝试乐观读,冲突时升级为悲观锁
五、典型案例
- 悲观锁:
java
// 使用ReentrantLock转账
public void transfer(Account from, Account to, int amount) {
lock.lock();
try {
from.withdraw(amount);
to.deposit(amount);
} finally {
lock.unlock();
}
}
- 乐观锁:
java
// 使用AtomicReference实现计数器
AtomicInteger counter = new AtomicInteger(0);
public void safeIncrement() {
int oldValue;
do {
oldValue = counter.get();
} while (!counter.compareAndSet(oldValue, oldValue + 1));
}
在分布式系统中,可结合Redis的WATCH/MULTI命令实现乐观锁,或通过数据库的版本号字段实现多版本并发控制(MVCC)。实际架构设计中,需结合QPS、数据一致性级别、重试成本等维度综合考量。