reentrantlock 比 synchronized 更灵活因其支持可中断等待、超时获取和公平性选择;需手动配对 lock() 和 unlock(),且 unlock() 必须置于 finally 块中;trylock() 用于非阻塞或超时获取;公平锁非默认,启用后吞吐量显著下降。

ReentrantLock 为什么能比 synchronized 更灵活
因为 synchronized 的加锁和释放完全绑定在 JVM 字节码层面:进入同步块自动加锁,退出(无论正常还是异常)自动释放。而 ReentrantLock 是一个 Java 类,锁的获取与释放由你显式调用 lock() 和 unlock() 控制,这就带来了三个关键自由度:可中断等待、超时获取、公平性选择。
必须手动配对调用 lock() 和 unlock(),且 unlock() 要放在 finally 块里
这是最容易出错的地方——忘记释放锁或在异常路径下跳过 unlock(),会导致死锁或线程饥饿。
正确写法只有一种主流模式:
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区逻辑
} finally {
lock.unlock(); // 即使抛异常也一定会执行
}
不推荐把 unlock() 放在 try 或 catch 里;也不要用“if (lock.isHeldByCurrentThread())”做二次检查来绕过 finally——这掩盖了设计缺陷,且 isHeldByCurrentThread() 本身不是原子判断条件。
用 tryLock() 实现非阻塞或带超时的锁获取
当你不想让线程无限等待,或者需要实现“尽力而为”的并发策略时,tryLock() 是核心工具。
-
tryLock()立即返回true(成功获取)或false(锁已被占用),不阻塞 -
tryLock(long timeout, TimeUnit unit)最多等待指定时间,超时返回false,期间可被中断 - 注意:超时版本抛
InterruptedException,必须处理,不能忽略
典型场景:后台任务轮询时避免卡死,或组合多个资源锁时做锁顺序回退。
公平锁不是默认选项,开启后显著影响吞吐量
ReentrantLock 默认是非公平锁(构造函数传 false 或无参),意味着新线程可能插队成功,抢在等待队列头之前拿到锁。这提升了平均吞吐,但可能导致个别线程长期饥饿。
如果你显式启用公平锁:new ReentrantLock(true),JVM 会严格按 FIFO 维护等待队列。实测中,在高竞争场景下,吞吐量可能下降 20%–50%,且线程调度开销上升。
除非业务明确要求“先到先服务”(比如订单扣减需严格按请求到达顺序),否则别开公平模式。它解决的是调度语义问题,不是线程安全问题。
真正难的不是写对 lock()/unlock(),而是判断哪些操作必须原子化、哪些可以拆解、以及要不要为灵活性牺牲确定性——这些没法靠 API 文档告诉你。










