ReentrantLock 是显式锁,需手动加锁解锁且支持公平锁、tryLock、条件队列等高级特性;synchronized 是隐式锁,JVM 自动管理,更简洁安全。

ReentrantLock 和 synchronized 有什么关键区别
ReentrantLock 是显式锁,必须手动 lock() 和 unlock();而 synchronized 是隐式锁,JVM 自动加锁/释放。这意味着一旦忘记 unlock(),就会永久阻塞其他线程——这是最常踩的坑。
它支持公平锁(new ReentrantLock(true))和非公平锁(默认),还提供 tryLock()、lockInterruptibly()、条件队列(newCondition())等能力,synchronized 没有这些。
性能上,高竞争场景下 ReentrantLock 可能更优(尤其配合自旋或超时重试),但日常低竞争代码中差异几乎不可测,别为“看起来更高级”而强行替换 synchronized。
必须用 try-finally 包裹 unlock() 吗
是的,必须。哪怕只有一行临界区代码,也得确保 unlock() 在异常路径下仍执行。Java 7+ 的 try-with-resources 不适用,因为 ReentrantLock 没实现 AutoCloseable。
立即学习“Java免费学习笔记(深入)”;
- 错误写法:
lock.lock(); doSomething(); lock.unlock();—— 若doSomething()抛异常,unlock()永远不执行 - 正确写法:
lock.lock(); try { doSomething(); } finally { lock.unlock(); } - 注意:不能把
lock()放进try块里——如果加锁失败(如被中断),finally会尝试对未持有锁的对象调用unlock(),抛IllegalMonitorStateException
什么时候该用 tryLock() 而不是 lock()
tryLock() 是非阻塞加锁,立即返回 true(成功)或 false(失败),适合避免死锁、实现限时等待、或构建“尽力而为”的并发逻辑。
典型场景包括:
- 两个资源需同时锁定时,用
tryLock()+ 回退机制防死锁 - 任务有严格超时要求,比如
tryLock(100, TimeUnit.MILLISECONDS) - 后台线程轮询检查状态,不想因锁阻塞影响调度周期
注意:tryLock() 成功后,仍需在 finally 中 unlock();失败时不要假设锁已释放——它根本没拿到锁。
Condition 与 Object.wait()/notify() 的实际差异
ReentrantLock.newCondition() 提供比 Object 原生监视器更精细的等待集控制。一个 ReentrantLock 可绑定多个 Condition 实例,比如生产者用 notFull、消费者用 notEmpty,互不干扰。
而 wait()/notify() 只能操作一个隐式等待队列,notify() 可能唤醒错误类型的线程,导致虚假唤醒或信号丢失。
使用要点:
-
await()必须在持有锁的前提下调用,且会自动释放锁;被唤醒后重新竞争锁 -
signal()不释放锁,也不立即唤醒线程,只是把线程从Condition队列移到 AQS 同步队列 - 永远在
while循环中检查条件(不是if),因为唤醒可能来自signalAll()或虚假唤醒
这点和 Object.wait() 一致,但 Condition 让你更容易写出语义清晰的多路等待逻辑。
真正难的不是写对语法,而是判断「这里到底需不需要显式锁」——多数业务代码用 synchronized 或 java.util.concurrent 工具类(如 ConcurrentHashMap、AtomicInteger)更安全。过早引入 ReentrantLock 容易把简单问题复杂化,尤其是锁的粒度、嵌套、中断响应这些细节,一不留神就埋下偶发性线程挂起问题。










