ReentrantLock的公平锁与非公平锁可间接用于流量整形:非公平锁通过“先抢后排”吸收短时脉冲,公平锁按FIFO保障长尾请求不饿死,混合策略支持运行时动态切换,但不可替代令牌桶等限流器。

ReentrantLock 的公平锁与非公平锁本身不是限流器,不能直接“平抑流量高峰”,但它们在高并发临界区调度上的行为差异,可被**间接利用**于流量整形策略中——关键在于理解其线程排队机制如何影响请求的“实际处理节奏”。真正起作用的是:用锁的阻塞特性制造可控延迟,配合队列行为调控请求吞吐节奏。下面分场景说明怎么用、怎么选、怎么防坑。
用非公平锁模拟“弹性缓冲”,吸收短时脉冲
非公平锁默认行为是“先抢后排”:新请求线程会优先尝试直接获取锁;若成功,立刻执行;失败才入队等待。这天然适配瞬时小高峰(如秒杀开闸前100ms涌入200个线程):
- 锁刚释放瞬间,CPU缓存里还留着上一个持有者的上下文,新线程CAS成功率高——部分请求“零延迟”通过,避免所有线程立刻堵死
- 未抢到的线程自动进入AQS FIFO队列,形成隐式缓冲区,把尖峰削成较平滑的处理流
- 相比手动维护阻塞队列+线程池,代码更轻量,无额外内存/调度开销
适用场景:后台任务批处理、日志落盘、缓存预热等对绝对顺序不敏感,但需扛住突发写压力的环节。注意要配合超时(tryLock(200, TimeUnit.MILLISECONDS))防止长尾请求无限等待。
用公平锁实现“确定性节流”,保障长尾请求不饿死
当瞬时高峰持续时间较长(如持续5秒以上),非公平锁可能让后到的线程反复插队,导致先排队的线程迟迟得不到服务(饥饿)。此时启用公平锁(new ReentrantLock(true))可强制按到达顺序处理:
- 每个请求在线程调用
lock()时即被记录入队位置,唤醒严格FIFO - 最大等待时间可估算:
队列长度 × 平均单次处理耗时,便于做SLA承诺 - 适合与熔断降级联动:一旦队列长度超阈值(如 > 50),拒绝新请求,避免雪崩
适用场景:支付确认、订单创建等强一致性要求、且用户感知延迟敏感的链路。需监控AQS队列长度(通过getQueueLength()),及时告警扩容。
混合策略:动态切换锁模式应对不同峰型
单一锁模式难适配所有流量特征。可设计运行时切换机制:
- 低峰期(QPS
- 检测到连续3秒队列长度 > 20 → 自动切换为公平锁,稳住长尾
- 恢复平稳后(队列清空且持续5秒无新增)→ 切回非公平锁
切换无需重启,只需用新锁实例替换旧引用(注意线程安全地更新引用),老请求继续用原锁完成,新请求走新锁。这是比单纯加队列更细粒度的“软限流”手段。
必须避开的典型误区
别把锁当限流器用错地方:
- 不替代令牌桶/漏桶:ReentrantLock无法限制QPS总数,只控制并发执行数。高QPS低耗时场景下,大量线程抢锁失败仍会频繁上下文切换,反而拖垮系统
- 不解决IO瓶颈:锁保护的是临界区代码,如果瓶颈在DB或远程调用,加锁只会让线程堆在锁外干等,应配合异步化或连接池优化
- 避免嵌套锁滥用:公平锁下多层lock()易引发隐式排队放大延迟,重入深度建议≤2层
本质上,这是用锁的调度语义做轻量级流量塑形,核心价值在于“不引入新组件的前提下,借已有并发原语达成可控延迟”。真正压不住的流量,还得靠前置网关限流和后端水平扩容。

















