侧边栏壁纸
博主头像
牧云

怀璧慎显,博识谨言。

  • 累计撰写 198 篇文章
  • 累计创建 19 个标签
  • 累计收到 8 条评论

目 录CONTENT

文章目录

非公平锁一定更好吗?揭秘公平锁的适用场景

秋之牧云
2026-05-14 / 0 评论 / 0 点赞 / 1 阅读 / 0 字

在 Java 并发编程中,ReentrantLocksynchronized 默认采用的都是非公平锁。许多开发者因此形成了一种刻板印象:非公平锁性能更好,是“默认且最佳”的选择。

然而,非公平锁并不一定在所有场景下都更好。虽然它在吞吐量上占据优势,但其固有的“插队”机制可能导致线程饥饿。本文将深入探讨非公平锁的局限性,并分析何时必须使用公平锁。

一、 为什么非公平锁是默认选择?

非公平锁(Non-fair Lock)允许新来的线程在锁释放的瞬间,直接通过 CAS 尝试获取锁,而无需进入等待队列 。这种机制带来了显著的性能优势:

  1. 更高的吞吐量:减少了线程上下文切换的开销。唤醒一个阻塞线程需要昂贵的内核态与用户态切换,如果新线程能直接获取锁并快速执行,就避免了这一过程 。
  2. 整体效率更高:在高竞争环境下,非公平锁的吞吐率通常远高于公平锁,因此在大多数通用场景下,它是更优解 。

二、 非公平锁的致命弱点:线程饥饿

非公平锁的核心缺陷在于不公平性可能导致线程饥饿

  • 插队现象:如果某些线程运气不好,每次准备获取锁时都有新线程“插队”,它们可能长期无法获得锁 。
  • 不确定性:获取锁的时间变得不可预测,对于对响应时间有严格要求的系统,这可能引发严重的延迟问题 。

三、 什么时候必须使用公平锁?

尽管公平锁(Fair Lock)因频繁的上下文切换导致吞吐量较低,但在以下场景中,它是不可或缺的选择:

1. 任务执行顺序敏感(如定时任务调度)

如果业务逻辑严格要求任务必须按照提交的顺序执行,公平锁是唯一选择。

  • 场景:定时任务调度、消息队列消费、数据库事务日志重放。
  • 原因:非公平锁可能导致后提交的任务先执行,破坏业务的时间序或因果序,导致数据不一致 。

2. 防止资源竞争中的线程饥饿

当多个服务或线程竞争共享资源,且持有锁的代码执行时间较长时,非公平锁可能导致部分线程“饿死”。

  • 场景:分布式锁竞争、共享连接池获取、硬件资源访问。
  • 原因:公平锁通过维护 FIFO(先进先出)队列,确保每个等待的线程最终都能获得锁,避免长期垄断 。

3. 限流与配额控制(保证 QoS)

在需要保证服务质量(QoS)的场景下,不能让某些请求永远“插队”成功。

  • 场景:API 网关速率限制、金融交易订单撮合。
  • 原因:非公平锁的等待时间是随机的,可能出现极长的尾延迟。公平锁提供了可预测的响应时间上限,确保所有用户请求得到公平对待 。

四、 总结与建议

特性公平锁 (Fair Lock)非公平锁 (Non-fair Lock)
获取顺序严格 FIFO随机,允许插队
吞吐量较低较高
线程饥饿不会发生可能发生
适用场景顺序敏感、防饥饿、长任务高并发、短任务、追求高性能

结论
基于对性能和公平性的权衡,如果你的应用对极致吞吐量敏感且任务简短,非公平锁是首选 。但如果你的系统需要保证顺序、防止饥饿提供可预测的响应时间,则必须使用公平锁

在实际架构设计中,除了切换锁策略,还可以考虑使用细粒度锁异步队列(如 Disruptor)来进一步优化并发性能,从而在公平性与效率之间找到最佳平衡点。

0

评论区