悲观锁和乐观锁

悲观锁(Pessimistic Lock), 顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。


乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。


两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适。

原文:http://blog.youkuaiyun.com/hongchangfirst/article/details/26004335

<think>嗯,用户现在想了解悲观锁乐观锁的概念。之前他们问过并行并发,看来他们对计算机科学中的并发控制机制感兴趣。现在需要详细解释这两个的概念,可能还要对比它们的区别应用场景。 首先,得明确悲观锁乐观锁的基本定义。悲观锁是假设最坏情况,每次操作数据时都加,防止其他线程访问。乐观锁则相反,假设冲突很少发生,只在提交时检查是否有冲突。需要举一些例子帮助理解,比如数据库中的行版本号机制。 接下来,用户可能需要知道它们的核心区别。比如,悲观锁适合写操作多的场景,而乐观锁适合读多写少的情况。还要提到它们的实现方式,比如数据库的SELECT FOR UPDATE是悲观锁,CAS操作或版本号是乐观锁。 另外,要考虑用户可能的实际应用场景。比如高并发下的库存扣减,悲观锁可能会导致性能问题,而乐观锁通过重试机制可能更高效。这时候需要举一些实际的例子,比如电商系统中的库存管理。 还要注意用户可能混淆两者的适用情况,需要明确指出优缺点。悲观锁的优点是简单直接,但容易引起阻塞;乐观锁减少竞争,但需要处理冲突重试,增加了复杂度。 最后,总结使用策略,帮助用户根据具体场景选择。比如在冲突频繁时用悲观锁,冲突较少时用乐观锁,或者结合两者使用。可能还需要提到一些技术实现,如Java中的synchronizedAtomic类,或者数据库的MVCC。 需要确保回答结构清晰,分点说明,对比明确,例子贴切。可能还需要检查是否有遗漏的关键点,比如CAS的具体过程,版本号如何工作,以及可能的问题如ABA问题及其解决方案。这样用户能全面理解这两个机制。</think>以下是关于**悲观锁****乐观锁**的对比解析,通过定义、实现逻辑适用场景帮助你理解两者的核心差异: --- ### **一、定义对比** 1. **悲观锁(Pessimistic Locking)** - **核心思想**:假设数据在操作时**一定会发生冲突**,因此**提前加**,阻止其他线程访问。 - **类比**:像“独占模式”——认为别人会修改数据,提前住资源。 - **典型场景**:银行转账、库存扣减(写操作频繁)。 2. **乐观锁(Optimistic Locking)** - **核心思想**:假设数据操作时**冲突概率低**,先不加,只在提交时检查数据是否被修改。 - **类比**:像“先操作再验证”——认为别人不会修改数据,通过版本号/时间戳判断冲突。 - **典型场景**:评论点赞、文档协作编辑(读多写少)。 --- ### **二、核心区别** | **特性** | **悲观锁** | **乐观锁** | |-----------------|-------------------------------------|-------------------------------------| | **冲突假设** | 默认高概率冲突,提前加 | 默认低概率冲突,提交时验证 | | **机制** | 显式加(如行、表) | 无显式,依赖版本号或CAS机制 | | **性能** | 高并发写场景下性能差(竞争) | 高并发读场景性能高(无竞争) | | **实现复杂度** | 简单(依赖数据库/语言内置) | 需手动处理冲突(重试或回滚) | | **数据一致性** | 强一致性 | 最终一致性 | --- ### **三、技术实现** 1. **悲观锁实现** - **数据库**:`SELECT ... FOR UPDATE`(行级) - **代码**:Java的`synchronized`关键字、`ReentrantLock` ```sql -- 示例:MySQL中定某行数据 BEGIN; SELECT stock FROM products WHERE id=1 FOR UPDATE; -- 加 UPDATE products SET stock=stock-1 WHERE id=1; COMMIT; ``` 2. **乐观锁实现** - **数据库**:版本号字段 + `WHERE`条件检查 - **代码**:CAS(Compare-And-Swap)、原子类(如`AtomicInteger`) ```sql -- 示例:通过版本号控制 UPDATE products SET stock=stock-1, version=version+1 WHERE id=1 AND version=current_version; -- 提交时验证版本 ``` ```java // Java中CAS实现(Atomic类) AtomicInteger atomicInt = new AtomicInteger(5); atomicInt.compareAndSet(5, 6); // 当前值为5时才更新为6 ``` --- ### **四、适用场景** 1. **优先选择悲观锁** - 写操作远多于读操作(如支付系统) - 数据强一致性要求高(如金融交易) 2. **优先选择乐观锁** - 读操作远多于写操作(如社交平台点赞) - 冲突概率低,且需要高吞吐量(如API限流计数器) 3. **混合使用** - 在分布式系统中,可结合两者: - 用乐观锁降低竞争 - 在关键操作(如余额修改)使用悲观锁 --- ### **五、典型问题与解决方案** 1. **悲观锁的缺点** - **死风险**:需设置超时时间(如MySQL的`innodb_lock_wait_timeout`) - **性能瓶颈**:通过缩小粒度(如行代替表)优化 2. **乐观锁的缺点** - **ABA问题**:版本号需递增,或用时间戳避免数值回滚 - **重试成本**:需设计补偿机制(如消息队列异步重试) --- ### **六、总结** - **悲观锁**:简单粗暴,适合“宁错杀不放过”的高冲突场景。 - **乐观锁**:轻量灵活,适合“相信世界平”的低冲突场景。 - **本质差异**:对并发冲突的**预判方式**不同,导致实现路径的分离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值