ReentrantLock通过“先自旋后阻塞”分层策略降低锁竞争损耗:短时轻竞争靠CAS+有限自旋,长时重竞争入队阻塞;配合tryLock超时、退避、临界区优化及慎用公平锁,实现高效并发控制。

ReentrantLock 本身不直接“消除”锁竞争,而是通过分层策略把激烈冲突带来的损耗降到最低——它不硬抗,而是聪明地分流:短时间轻竞争靠自旋抢,长时间重竞争靠队列等,再配合开发者主动干预手段,把系统从“排队堵死”拉回“有序通行”。
非公平模式下自动启用“先自旋后阻塞”
这是 ReentrantLock 应对突发高争用最核心的缓冲机制:
- 线程调用 lock() 时,先用 CAS 尝试一次获取锁;成功就直接进入临界区,零开销
- 失败后不立刻挂起,而是在用户态做有限轮次自旋(JVM 硬编码,通常几十次),持续检查锁是否释放
- 自旋耗尽仍没拿到锁,才封装为 Node 加入 AQS 同步队列,调用 LockSupport.park() 进入阻塞
这个设计让毫秒级、低碰撞的临界区几乎不触发上下文切换,CPU 利用率更健康,吞吐更稳。
用 tryLock 配合超时和退避,避免线程无限等待
当锁竞争已明显激烈(比如压测中大量线程卡在 BLOCKED),硬等 lock() 会加剧调度压力。此时应主动降级:
- 用 tryLock(100, TimeUnit.MILLISECONDS) 设定合理超时,拿不到就放弃或走降级逻辑(如返回缓存值、记录告警)
- 若需重试,别用 while(true) + tryLock() 空转,应加入指数退避(如第一次等 1ms,第二次 2ms,第三次 4ms…)
- 对关键路径,可结合 lockInterruptibly(),让上游调用方能主动中断等待,防止雪崩式阻塞
别只盯锁参数,重点优化临界区本身
再好的锁机制也扛不住长临界区。真正缓解激烈冲突,80% 功夫在线下:
- 检查临界区内是否混入了 IO、远程调用、大循环或复杂计算——这些必须移出锁外
- 评估锁粒度:是整个对象一把锁?能否拆成多个细粒度锁(如按 ID 分段、用 ConcurrentHashMap 替代同步容器)
- 确认是否真需要互斥写:读多写少场景优先考虑 StampedLock 或 ReentrantReadWriteLock
- 查日志或 jstack,确认没有 忘记 unlock() 导致锁被永久占住——这是伪高竞争的常见根因
公平锁不是解药,慎用于高并发场景
虽然 new ReentrantLock(true) 能保证 FIFO,但它在真实高争用下反而加重负担:
- 每次 lock() 都要检查队列状态、确保入队尾部,开销比非公平锁高 2–5 倍
- 上下文切换更频繁,CPU 缓存局部性变差,实测吞吐可能只剩非公平锁的 1/3
- 公平性只解决“排队顺序”,不解决“前面的人干了 5 秒”这种持锁过长问题
除非业务强要求严格顺序(如任务调度器),否则默认非公平锁 + 合理临界区控制,才是高并发下的务实选择。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











