面试知识库
极高 进阶

ReentrantLock与synchronized对比#

一句话答案#

ReentrantLock 支持公平锁/可中断等待/超时获取/多条件变量,synchronized 是关键字自动释放更简洁。

核心要点
维度synchronizedReentrantLock
实现层面JVM 内置关键字,C++ 实现Java 代码实现(AQS)
锁获取自动(进入同步块时)手动(lock()
锁释放自动(退出同步块/异常时,由 JVM 保证)手动(unlock(),必须写在 finally 中)
是否可中断不可中断(lock() 会无限等待)可中断(lockInterruptibly()
是否可超时不支持支持(tryLock(timeout, unit)
是否公平非公平(不保证等待顺序)可选(new ReentrantLock(true) 为公平锁)
条件变量只有一个隐式的(wait/notify)多个(newCondition(),精确唤醒)
性能JDK 6 后优化,差距已不大差距不大,高竞争场景略优
锁升级支持(偏向→轻量→重量)不支持(直接 CAS + AQS)

正确使用 ReentrantLock 模板:

ReentrantLock lock = new ReentrantLock();
lock.lock();        // 获锁
try {
    // 业务逻辑
} finally {
    lock.unlock();  // 必须在 finally 中释放,防止异常导致永久持锁
}
java

选择建议:

  • 大多数场景用 synchronized(更简洁,JVM 自动管理,不会忘记释放)
  • 需要以下特性时用 ReentrantLock:超时获取锁、可中断等待、公平锁、多个条件变量
面试回答(2分钟版)

ReentrantLock 和 synchronized 都是可重入的互斥锁,但实现层面和功能丰富度不同。synchronized 是 JVM 内置关键字,通过 monitorenter/monitorexit 字节码实现,锁的获取和释放完全自动,即使发生异常 JVM 也能保证释放,JDK6 之后引入了偏向锁、轻量级锁等优化,性能已经很好。ReentrantLock 是 Java 层面基于 AQS 实现的,需要手动 lock/unlock 且 unlock 必须写在 finally 块中,但它提供了四个 synchronized 不具备的能力:一是可中断等待 lockInterruptibly(),二是超时获取 tryLock(timeout, unit),三是可以选择公平锁 new ReentrantLock(true),四是支持多个 Condition 条件变量实现精确唤醒。实际开发中大多数场景我会优先用 synchronized,代码更简洁也更安全;只有在需要超时获取、可中断等待、公平调度或多条件变量时才会切换到 ReentrantLock。

追问与易错

追问方向:

  • “什么时候必须用 Lock?”→ 需要超时获取锁(tryLock)、可中断等待(lockInterruptibly)、公平锁、多个 Condition 精确唤醒时,synchronized 无法满足
  • “公平锁为什么性能差?”→ 每次获锁都要检查 AQS 等待队列是否有排队线程,不允许插队,导致大量线程上下文切换;非公平锁允许新线程直接 CAS 抢锁,减少切换开销
  • “怎么实现可重入的?”→ AQS 的 state 字段做重入计数,每次 lock 时检查持有者是否是当前线程,是则 state+1;unlock 时 state-1,减到 0 才真正释放锁

易错点:

  • ❌ ReentrantLock 一定比 synchronized 好——简单场景 synchronized 更安全
  • ❌ 忘记在 finally 中 unlock