乐观锁与悲观锁是两种并发控制思想:悲观锁假设冲突必然发生,操作前加锁阻塞其他线程,适用于写多、高冲突、强一致场景;乐观锁假设冲突极少,不加锁,提交时校验版本或CAS,适用于读多、低冲突、可重试场景。

乐观锁和悲观锁不是具体某把“锁”,而是两种应对并发问题的设计思想。核心区别在于:一个提前加锁防冲突,一个先干再说、提交时再验。
悲观锁:先锁住,再操作
悲观锁认为“别人随时会改数据”,所以一上来就抢锁,确保自己独占资源。锁没拿到,线程就得等——这是典型的阻塞式行为。
-
典型实现:数据库的
SELECT ... FOR UPDATE、Java 的synchronized、ReentrantLock - 适用场景:写操作频繁、冲突概率高、强一致性不可妥协的地方,比如银行转账、秒杀扣库存
- 注意点:容易引发死锁或长事务阻塞;锁粒度越细(如行锁)性能越好,但管理更复杂
乐观锁:不锁,只校验
乐观锁相信“多数时候没人跟我抢”,读数据时不加锁,更新时才比对数据是否被改过。如果发现已被动过手脚,就放弃或重试。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
-
典型实现:数据库加
version字段 + WHERE version = ?;Java 的AtomicInteger(底层是 CAS);JPA 中的@Version注解 - 适用场景:读多写少、冲突率低、可接受短暂不一致的业务,比如文章点赞数、用户资料轻量更新
- 注意点:高冲突下重试频繁会浪费 CPU;CAS 存在 ABA 问题(值变回原样但中间已变化),需配合带时间戳的原子引用解决
怎么选?看冲突频率和一致性要求
没有绝对好坏,关键看业务特征:
立即学习“Java免费学习笔记(深入)”;
- 若写操作占比超过 30%、失败重试代价大、必须零误差——选悲观锁
- 若读操作远多于写、冲突极少、允许少量重试——选乐观锁更轻量高效
- 混合场景可分层处理:比如用乐观锁做初筛,冲突后降级为悲观锁兜底
实际代码中常见搭配
真实项目往往不是非此即彼:
- 数据库层用
FOR UPDATE防超卖,应用层用@Version避免覆盖式更新 - 计数类高频读写用
AtomicLong(乐观+CAS),避免 synchronized 带来的串行瓶颈 - 分布式环境下单机锁失效,常结合 Redis 分布式锁(悲观)或 ZooKeeper 临时节点(类乐观)来协同

















