
乐观锁和悲观锁详解
1. 核心概念对比
悲观锁(Pessimistic Lock)
• 定义:
假设共享资源每次访问都会发生冲突,每次操作前加锁,其他线程需阻塞等待锁释放。
• 类比现实:
类似“未雨绸缪”的人,总假设最坏情况,提前预防问题。
• 实现方式:
Java 中通过 synchronized
和 ReentrantLock
等独占锁实现。
代码示例
// synchronized 示例
public void performSynchronisedTask() {
synchronized (this) {
// 需要同步的操作
}
}
// ReentrantLock 示例
private Lock lock = new ReentrantLock();
lock.lock();
try {
// 需要同步的操作
} finally {
lock.unlock();
}
优缺点
• 优点:避免数据竞争,保证强一致性。
• 缺点:
• 高并发下锁竞争激烈,导致线程阻塞和上下文切换,性能开销大。
• 可能引发死锁(如锁顺序不当)。
乐观锁(Optimistic Lock)
• 定义:
假设共享资源访问不会冲突,操作时不加锁,提交修改时验证数据是否被修改。
• 类比现实:
类似“乐观”的人,假设最好情况,快速解决问题。
• 实现方式:
通过 版本号机制 或 CAS 算法 实现。Java 中如 AtomicInteger
和 LongAdder
。
代码示例
// LongAdder 示例(高并发性能更优,空间换时间)
LongAdder sum = new LongAdder();
sum.increment();
优缺点
• 优点:
• 无锁竞争,无死锁问题,读多写少场景性能更优。
• 缺点:
• 写冲突频繁时,大量失败重试,影响性能(可通过 LongAdder
等优化)。
• 仅适用于单共享变量场景。
2. 乐观锁实现技术
版本号机制
• 原理:
数据表中添加 version
字段,每次修改 version+1
。提交更新时,校验当前 version
是否与读取时一致。
• 示例:
账户余额修改场景中,通过版本号避免并发更新覆盖问题。
CAS 算法(Compare And Swap)
• 原理:
原子操作,依赖 CPU 指令,比较变量当前值(V)与预期值(E),相等时更新为新值(N)。
• 三要素:
V
(变量值)、E
(预期值)、N
(新值)。
• 特点:
• 失败线程可重试或放弃,无阻塞。
• 需处理 ABA 问题(需额外机制如版本号)。
3. 适用场景总结
锁类型 | 适用场景 | 典型实现 |
---|---|---|
悲观锁 | 写多读少,竞争激烈 | synchronized , ReentrantLock |
乐观锁 | 读多写少,冲突较少 | AtomicInteger , LongAdder |
4. 扩展阅读
• 《Java 并发编程核心 78 讲》
涵盖悲观锁、乐观锁、可重入锁、自旋锁等详细实现与实战分析。