高并发下的锁选择:乐观锁 vs 悲观锁全面对比

目录

1. 乐观锁 (Optimistic Lock)

核心思想

实现机制

代码示例

优缺点

适用场景

2. 悲观锁 (Pessimistic Lock)

核心思想

实现机制

代码示例

优缺点

适用场景

3. 公平锁 (Fair Lock) vs 非公平锁 (Non-fair Lock)

公平锁

非公平锁

对比表格

4. 综合对比


1. 乐观锁 (Optimistic Lock)

核心思想

先操作,后检查冲突。假设并发冲突的概率很低,允许多个线程同时访问资源,只在提交时检查是否有冲突。

实现机制

  1. 版本号/时间戳:为数据增加一个版本字段

  2. CAS操作:Compare-And-Swap,比较并交换

  3. 流程:读取数据 → 修改数据 → 验证版本号 → 如果验证通过则提交,否则重试或失败

代码示例

UPDATE products 
SET stock = stock - 1, version = version + 1 
WHERE id = 100 AND version = 5;
// Java CAS示例
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.compareAndSet(0, 1); // 如果当前值是0,则设置为1

优缺点

优点

  1. 并发性能高,减少线程阻塞

  2. 适合读多写少的场景

缺点

  1. 冲突时需要重试,可能产生自旋开销

  2. 不保证操作一定成功

适用场景

  1. 数据库并发更新

  2. 购物车库存扣减

  3. 社交媒体点赞功能

2. 悲观锁 (Pessimistic Lock)

核心思想

先加锁,后操作。假设并发冲突的概率很高,每次操作前先获取锁,确保独占访问。

实现机制

  1. synchronized:Java内置锁

  2. ReentrantLock:可重入锁

  3. 数据库行锁:SELECT ... FOR UPDATE

代码示例

public synchronized void criticalSection() {
    // 线程安全代码
}

// Java ReentrantLock
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
    // 线程安全代码
} finally {
    lock.unlock();
}
 
START TRANSACTION;
SELECT * FROM accounts WHERE id = 1 FOR UPDATE;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
COMMIT;

优缺点

优点

  1. 保证数据强一致性

  2. 编程模型简单

缺点

  1. 并发性能较低,线程阻塞

  2. 可能产生死锁

适用场景

  1. 银行转账交易

  2. 订单支付处理

  3. 需要强一致性的金融系统

3. 公平锁 (Fair Lock) vs 非公平锁 (Non-fair Lock)

公平锁

按照请求顺序分配锁,先到先得。

ReentrantLock fairLock = new ReentrantLock(true); // true表示公平锁

特点

  1. 线程按申请顺序获取锁

  2. 不会出现线程饥饿现象

  3. 性能相对较低(需要维护队列)

非公平锁

允许插队,后申请的线程可能先获取到锁。

ReentrantLock nonFairLock = new ReentrantLock(); // 默认非公平锁
ReentrantLock nonFairLock = new ReentrantLock(false);

特点

  1. 吞吐量更高,减少线程切换

  2. 可能造成线程饥饿

  3. 性能更好

对比表格

特性公平锁非公平锁
获取顺序先进先出允许插队
性能较低较高
线程饥饿不会可能
实现复杂度较高较低

4. 综合对比

维度乐观锁悲观锁
并发性
开销冲突时开销大始终有开销
数据一致性最终一致性强一致性
适用场景读多写少写多读少
实现方式版本号、CASsynchronized、ReentrantLock
<think>好的,我现在需要回答用户关于分布式乐观锁悲观锁的概念、区别、原理及使用场景的问题。首先,我得回忆一下这三种的基本定义,然后根据提供的引用资料来整理信息,确保准确性和全面性。 首先,悲观锁。根据引用[1][2][4],悲观锁的核心思想是假设最坏的情况,每次操作数据时都认为会有其他并发操作,因此先加再操作。数据库中的SELECT FOR UPDATE就是典型例子,适用于写操作频繁的场景,比如账户余额更新,避免数据冲突,但可能降低并发性能。 然后是乐观锁,引用[1][3][4]提到,乐观锁假设冲突较少,通过版本号或时间戳机制实现,比如CAS操作。适用于读多写少的情况,比如库存扣减,先操作后检查版本,减少了的开销,但需要处理更新失败的情况。 分布式,引用[1][3][4]指出,用于分布式系统中跨进程的资源控制,Redis的Redisson实现就是例子,支持可重入、自动续期,适用于秒杀系统或分布式任务调度,相比数据库性能更好,但需要处理网络延迟等问题。 接下来是区别部分。原理上,悲观锁依赖数据库乐观锁用版本号,分布式用外部存储如Redis。使用场景方面,根据引用[3][4],悲观锁适合高并发写,乐观锁适合读多写少,分布式适合跨系统。性能上,乐观锁和分布式通常比悲观锁高效,但各有适用条件。 需要确保回答结构清晰,分点说明,并引用相关资料。同时要生成相关问题,可能涉及应用场景选择、实现方式对比等。最后检查引用标识是否正确,避免遗漏。可能还需要注意用户提供的引用中的一些细节,比如Redisson的具体功能,以及不同的适用场景对比。</think>### 分布式乐观锁悲观锁详解 #### 一、基本概念与原理 1. **悲观锁** - **原理**:假设并发操作必然发生冲突,通过**先加后操作**的方式确保数据独占性。例如数据库的 `SELECT ... FOR UPDATE` 语句会直接定记录[^2][^4]。 - **实现**:依赖数据库机制(如行、表),或编程语言的互斥(如Java的 `synchronized`)。 2. **乐观锁** - **原理**:假设冲突概率低,通过**版本控制**(如时间戳、版本号)实现无化操作。例如更新数据时检查版本是否匹配,若冲突则重试或回滚[^3][^4]。 - **实现**:常用CAS(Compare And Swap)算法,如MySQL的 `UPDATE table SET value=new_value, version=version+1 WHERE id=1 AND version=old_version`。 3. **分布式** - **原理**:在分布式系统中协调多个节点对共享资源的访问,通常基于**外部存储系统**(如Redis、ZooKeeper)实现的互斥性。例如Redisson的 `RLock` 通过Lua脚本保证原子性[^1][^3]。 - **实现**:Redis的 `SETNX` 指令、Redisson的可重入、ZooKeeper的临时有序节点。 --- #### 二、核心区别 | 维度 | 悲观锁 | 乐观锁 | 分布式 | |--------------|-------------------------|-------------------------|--------------------------| | **冲突假设** | 高概率冲突 | 低概率冲突 | 跨进程/节点冲突 | | **加时机** | 操作前加 | 操作后检查版本 | 通过外部系统协调加 | | **性能影响** | 高(阻塞其他操作) | 低(无,可能需重试) | 中等(依赖网络通信) | | **适用场景** | 写操作频繁(如转账)[^2] | 读多写少(如库存扣减)[^3] | 分布式任务调度、秒杀系统[^1] | --- #### 三、典型应用场景 1. **悲观锁** - 银行账户余额更新(避免并发扣款冲突)[^2]。 - 订单状态修改(确保同一订单不会被重复处理)。 2. **乐观锁** - 电商库存扣减(版本号防止超卖)[^3]。 - 文档协同编辑(通过时间戳检测内容冲突)。 3. **分布式** - 分布式定时任务(防止多节点重复执行)[^1]。 - 秒杀系统中商品抢购(Redis分布式控制并发请求)。 --- #### 四、选型建议 - **单体应用**:优先选择数据库悲观锁(简单可靠)[^3]。 - **高并发读场景**:使用乐观锁减少开销[^4]。 - **分布式系统**:Redis分布式(性能高,支持自动续期)或ZooKeeper(强一致性)[^1][^3]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值