网站首页 > java教程 正文
乐观锁与悲观锁的对比及实现方式
一、核心概念对比
维度 | 悲观锁 | 乐观锁 |
设计理念 | 假设并发冲突必然发生,预先加锁防止数据修改 | 假设并发冲突较少发生,提交时检测冲突 |
适用场景 | 写操作频繁,数据竞争激烈 | 读多写少,冲突概率低 |
性能开销 | 加锁/释放锁带来较大开销,高并发下可能成为瓶颈 | 无锁设计,冲突检测时可能有重试开销 |
数据一致性 | 强一致性 | 最终一致性 |
二、实现方式详解
1. 悲观锁实现
Java 原生支持:
- synchronized 关键字
public synchronized void updateData() { // 临界区操作(自动加锁/解锁) }
- ReentrantLock
ReentrantLock lock = new ReentrantLock();
public void updateData() { lock.lock();
try { // 临界区操作
} finally {
lock.unlock();
} }
数据库实现:
- SELECT ... FOR UPDATE(行级锁)
BEGIN;
SELECT * FROM account WHERE id = 1 FOR UPDATE;
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;
2. 乐观锁实现
Java CAS 操作:
- Atomic 原子类
AtomicInteger count = new AtomicInteger(0);
public void safeIncrement() {
int oldValue, newValue;
do {
oldValue = count.get();
newValue = oldValue + 1;
} while (!count.compareAndSet(oldValue, newValue));
}
数据库版本号机制:
- Version 字段控制
UPDATE product
SET stock = stock - 1,
version = version + 1
WHERE id = 100 AND version = 2; -- 检查影响行数,若为0则重试
框架支持:
- JPA/Hibernate
@Entity public class Product {
@Id private Long id;
private int stock;
@Version private int version; // 自动管理版本号
}
三、性能对比与选择策略
场景 | 推荐锁类型 | 理由 |
短事务高频写 | 悲观锁 | 避免频繁冲突导致的重试开销(如秒杀库存扣减) |
长事务复杂操作 | 乐观锁 | 减少锁持有时间,防止长事务阻塞(如订单状态流转) |
读多写少 | 乐观锁 | 无锁设计提升吞吐量(如商品详情浏览) |
分布式系统 | 乐观锁 | 避免跨服务锁管理的复杂性(如基于版本号的分布式事务) |
四、实现方案最佳实践
1. 高并发秒杀(悲观锁)
java
public boolean seckill(Long productId) {
// 使用分布式锁(如Redisson)
RLock lock = redisson.getLock("seckill:" + productId);
try {
lock.lock(3, TimeUnit.SECONDS); // 控制锁超时时间
Product product = productDao.selectForUpdate(productId);
if (product.getStock() > 0) {
productDao.updateStock(productId);
return true;
}
return false;
} finally {
lock.unlock();
}
}
2. 购物车商品数量修改(乐观锁)
java
public boolean updateCartQuantity(Long cartId, int newQty) {
Cart cart = cartDao.get(cartId);
int retry = 0;
while (retry < 3) { // 最大重试次数
int oldVersion = cart.getVersion();
cart.setQuantity(newQty);
int rows = cartDao.updateWithVersion(
cartId, newQty, oldVersion, oldVersion + 1
);
if (rows > 0) return true;
cart = cartDao.get(cartId); // 重新加载最新数据
retry++;
}
return false;
}
五、常见面试追问及应答
Q1:CAS操作有什么缺点?
- ABA问题:通过版本号或时间戳解决(如AtomicStampedReference)
- 自旋开销:冲突激烈时CPU占用高,需配合退避策略(如指数退避)
Q2:如何选择数据库乐观锁的实现方式?
- 版本号字段:需额外维护字段,适合业务数据模型
- 时间戳:精度可能不足,存在并发风险
- 条件更新:直接在WHERE子句中校验原值(如WHERE stock = oldStock)
Q3:分布式场景下如何实现乐观锁?
- Redis WATCH/MULTI:监控Key变化后执行事务
- ZooKeeper版本号:znode的dataVersion属性控制
- 分布式事务框架:Seata的AT模式通过全局锁实现
总结
- 悲观锁:通过独占资源确保强一致性,适合写冲突频繁场景,但需注意死锁风险和性能损耗。
- 乐观锁:通过版本控制实现无锁并发,适合读多写少场景,需处理冲突重试和ABA问题。
技术选型建议:
- 优先评估业务场景的读写比例和冲突概率
- 简单场景用语言级锁(如synchronized),复杂场景用框架支持(如JPA乐观锁)
- 分布式系统需结合中间件(如Redis/ZooKeeper)实现跨服务锁管理
猜你喜欢
- 2025-06-23 Java阻塞队列:LinkedTransferQueue
- 2025-06-23 Java面试题-锁的膨胀升级过程(锁的机制升降级)
- 2025-06-23 Java中CurrentHashMap的使用原理?
- 2025-06-23 Java并发锁机制和Atomic原理解析(java并发的时候常用的处理方式)
- 2025-06-23 「Java多线程」内置锁(Synchronized)的前世今生
- 2025-06-23 万字详文:腾讯高可用、高性能 ZooKeeper 源码和实践揭秘
- 2025-06-23 ReentrantLock源码解析:ReentrantLock 的实现原理与 AQS 机制
- 2025-06-23 Java面试题整理:紧跟2025年面试趋势
- 2025-06-23 面试官:说一下 synchronized 锁机制原理 与 Lock 锁机制
- 2025-06-23 【锁思想】自旋 or CAS 它俩真的一样吗?一文搞懂
你 发表评论:
欢迎- 最近发表
-
- java8的stream使用小示例(java stream的用法)
- Java 中的 Lambda 表达式深入解析:从语法糖到高阶函数
- 实战解析Android架构设计原则(android的架构)
- 搭建mcp服务器用java17,可是项目开发用的是java8怎么实现?
- Java Stream:集合处理的api(java集合流操作)
- Java 8新特性全面剖析:让编程变得更简单优雅
- Java 8新特性全面解析与最佳实践:掌握未来编程的艺术
- Java 8日期时间API新特性揭秘与深度解析
- Java 8 Stream API 详解(java.stream)
- Java机器学习库(Java ML)(二、聚类)
- 标签列表
-
- java反编译工具 (77)
- java反射 (57)
- java接口 (61)
- java随机数 (63)
- java7下载 (59)
- java数据结构 (61)
- java 三目运算符 (65)
- java对象转map (63)
- Java继承 (69)
- java字符串替换 (60)
- 快速排序java (59)
- java并发编程 (58)
- java api文档 (60)
- centos安装java (57)
- java调用webservice接口 (61)
- java深拷贝 (61)
- 工厂模式java (59)
- java代理模式 (59)
- java.lang (57)
- java连接mysql数据库 (67)
- java重载 (68)
- java 循环语句 (66)
- java反序列化 (58)
- java时间函数 (60)
- java是值传递还是引用传递 (62)
本文暂时没有评论,来添加一个吧(●'◡'●)