reentrantlock 缓解性能抖动的关键在于控锁而非加锁:需缩小临界区、避免锁内耗时操作、用condition替代wait/notifyall消除惊群效应、按需选择非公平锁、优先使用无锁原子类,并从源头减少共享。

ReentrantLock 能有效缓解资源竞争引发的性能抖动,关键不在“加锁”,而在“控锁”——控制锁的粒度、范围、唤醒方式和退出路径。
缩小临界区,避免锁内耗时操作
性能抖动常源于线程在锁内做 I/O、远程调用或长循环,导致其他线程长时间排队等待。应只把真正需要互斥的代码包进 lock/unlock 之间。
- 错误示例:在 synchronized 或 lock 块里调用 httpClient.execute() 或 queryDB()
- 正确做法:先获取必要参数 → 释放锁 → 再发起网络/数据库调用
- 务必用 try-finally 或 try-with-resources 确保 unlock() 执行,防止死锁
用 Condition 替代 wait/notifyAll,消除惊群效应
传统 synchronized + notifyAll() 容易唤醒所有等待线程,但实际只有一人能干活,其余线程抢锁失败或抢到后发现无事可做,徒增上下文切换(cs 飙升)。
- 改用 ReentrantLock.newCondition() 创建独立条件队列
- 生产者放完数据后调用 condition.signal()(不是 signalAll)
- 消费者在 while(空) 条件下 await(),确保虚假唤醒后重新校验
- 典型结构:一个锁配多个 Condition(如 notEmpty / notFull),读写分离唤醒
按场景选锁策略,不盲目追求公平
非公平锁(默认)允许新线程“插队”获取锁,虽牺牲一点顺序性,但显著减少线程挂起/唤醒开销,在高并发下吞吐更高。
- 仅当业务明确要求等待顺序(如任务调度优先级严格)才启用公平锁 new ReentrantLock(true)
- 避免在锁竞争激烈时频繁调用 lockInterruptibly() 或 tryLock(timeout),它们本身会引入额外判断开销
- 对简单计数、状态标记等场景,优先用 AtomicInteger 或 LongAdder,比加锁快数倍
从源头减少共享,比优化锁更治本
锁只是兜底手段。真正降低抖动,要设法让线程少争同一块资源。
- 用 ThreadLocal 存储线程私有对象(如 SimpleDateFormat、数据库连接)
- 对高频写场景做分片:比如将全局计数器拆成 N 个 AtomicLong,取模分配,最后汇总
- 异步化:将非核心逻辑(日志记录、监控上报)转为异步队列处理,避免阻塞主流程
- 参考 JDK 实现:LinkedBlockingQueue 用 takeLock/putLock 双锁分离读写,天然抗惊群
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











