reentrantlock 是 synchronized 的补充而非替代,优势在于可中断加锁、公平锁控制、多 condition 精准唤醒及超时获取锁;它需手动释放锁,而 synchronized 由 jvm 自动管理。

ReentrantLock 不是 synchronized 的替代品,而是补充。它在需要精细控制线程行为的场景中展现出明显优势:比如必须响应中断、不能无限等待、要按顺序排队,或者需对不同业务逻辑做独立唤醒时。
公平锁:按申请顺序排队,避免饥饿
ReentrantLock 可通过构造函数指定是否启用公平模式:new ReentrantLock(true)。公平锁内部严格遵循 FIFO 原则,等待最久的线程优先获取锁;而非公平锁(默认)允许新线程直接尝试抢占,成功则跳过队列,提升吞吐量但可能造成个别线程长期得不到执行(即“饥饿”)。
适合用公平锁的情况包括:任务处理时效性敏感、资源分配需强一致性、或调试/测试阶段需要可预测的调度行为。
- 非公平锁性能通常更高——因减少了线程挂起(park)和唤醒(unpark)次数,避免频繁上下文切换
- 公平锁虽降低吞吐,但能缓解长等待线程被持续忽略的问题
- 注意:公平性只作用于队列内线程,不保证与已持有锁的线程之间的时间先后
可中断加锁:主动退出阻塞,支持优雅停机
synchronized 在等待锁时无法响应 interrupt,一旦阻塞就只能等锁释放;而 ReentrantLock 提供 lockInterruptibly() 方法,使线程在等待过程中收到中断信号时立即抛出 InterruptedException 并退出。
这在服务关闭、超时熔断、用户取消操作等场景中至关重要。
- 调用后需捕获异常,并建议恢复中断状态:
Thread.currentThread().interrupt() - 不能在持有锁后才检查中断,必须在获取锁的过程中响应
- 与
tryLock()不同,它是被动响应中断,不是主动设定等待时限
多 Condition 等待条件:精准唤醒,解耦等待逻辑
synchronized 只能配合单一 wait/notify 机制,所有等待线程共用一个队列;ReentrantLock 支持通过 lock.newCondition() 创建多个独立的 Condition 实例,每个对应不同的等待语义。
例如生产者-消费者模型中,可用 notFull 和 notEmpty 两个 Condition 分别管理缓冲区满/空的等待线程,实现“只唤醒相关方”。
- 每个 Condition 维护自己的等待队列,signal() 只唤醒该队列中的线程
- await() 会自动释放锁并进入等待;signal() 后线程被唤醒时需重新竞争锁
- 避免了 notifyAll() 引发的“惊群效应”,减少无谓的锁竞争
对比 synchronized 的关键差异点
二者都支持可重入,但底层机制和能力边界不同:
- synchronized 是 JVM 层实现,自动加解锁,无需手动管理;ReentrantLock 是 JDK 层实现,必须显式调用 unlock(),且推荐放在 finally 块中防止泄漏
- 新版本 JVM 对 synchronized 做了大量优化(如锁消除、偏向锁、轻量级锁),性能差距已大幅缩小,多数简单同步场景仍推荐用 synchronized
- ReentrantLock 的 tryLock(timeout) 提供超时能力,synchronized 没有等价机制,只能靠外部定时器+中断间接实现
- ReentrantLock 的扩展性更强,可结合 AQS 自定义同步器,而 synchronized 功能固定不可扩展










