公平锁与非公平锁有什么区别?

该文章已生成可运行项目,

公平锁与非公平锁是并发编程中两种核心的锁策略,主要区别在于锁分配的公平性机制。以下是基于底层原理、性能表现和应用场景的详细对比:

1. 定义与核心区别

  • 公平锁:严格遵循 ​​FIFO(先来先得)​​ 原则。线程按请求锁的顺序排队,新请求的线程必须加入等待队列末尾,只有轮到其位置时才能获取锁。

    特点:避免线程饥饿(Starvation),保证公平性;但性能开销较大

  • 非公平锁: 允许​​线程插队​​抢占锁。新线程可直接尝试获取锁(即使等待队列中有其他线程),成功则立即执行;失败才加入队列等待。

    特点:吞吐量更高,但可能导致部分线程长时间无法获取锁(饥饿现象)

2. 底层实现差异(以 Java 的 ReentrantLock 为例)

公平锁
  • 核心方法tryAcquire()中调用 hasQueuedPredecessors()`,检查当前线程是否是等待队列的首位。

  • 若队列非空且当前线程不在队首,则获取锁失败,线程加入队列等待。

    protected final boolean tryAcquire(int acquires) {
        if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(currentThread);
            return true;
        }
        return false;
    }
非公平锁
  • 直接通过 CAS(Compare-And-Swap) 尝试抢占锁,无视等待队列

    final void lock() {
        if (compareAndSetState(0, 1)) // 尝试直接抢锁
            setExclusiveOwnerThread(currentThread());
        else
            acquire(1); // 失败后进入队列
    }
  • 优势:减少线程切换开销,避免从运行态切换到阻塞态

3. 性能与吞吐量对比

指标公平锁非公平锁
吞吐量较低(需维护队列顺序)更高(减少线程切换)
延迟稳定性更稳定(按序执行)波动大(插队导致不确定性)
线程饥饿不会发生可能发生

实测数据:在 64 线程高并发场景下,非公平锁的吞吐率可达公平锁的 10 倍以上

4. 适用场景

公平锁适用场景
  • 顺序敏感型任务:如打印任务队列、数据库事务顺序处理。

  • 避免饥饿的实时系统:如金融交易系统,确保每个请求按序处理

非公平锁适用场景
  • 高吞吐需求:如缓存系统(Redis)、线程池任务调度。

  • 短期锁竞争:锁持有时间短时,插队能显著提升响应速度

5. Java 中的默认行为

  • ReentrantLock`:

    • 默认非公平锁(new ReentrantLock())。

    • 需显式指定公平性:new ReentrantLock(true)。

  • synchronized: 底层通过 ​ObjectMonitor​ 实现,​​仅支持非公平锁​​,无法配置为公平锁

总结:核心区别与选择建议

维度公平锁非公平锁
锁分配原则严格 FIFO,先请求先获得允许插队,新线程可抢占
性能优先级公平性 > 性能性能 > 公平性
适用场景顺序敏感、避免饥饿高并发、高吞吐需求
实现复杂度需维护等待队列,开销大实现简单,减少上下文切换

📌 建议

  • 优先选择非公平锁:在大多数高并发场景中,其性能优势显著(Java 默认策略即是证明)。

  • 仅当业务强需求时使用公平锁:如订单处理、审计日志等严格顺序场景

本文章已经生成可运行项目
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tsxchen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值