ReentrantLock 与 synchronized 有何区别?如何选择?
结论先行:两者都是可重入互斥锁,功能高度重叠;ReentrantLock 是 API 层面的显式锁(lock/unlock 配对使用),额外提供可中断、可超时、公平/非公平、多条件队列等能力;synchronized 是语法层面的隐式锁,简单可靠且随 JDK 持续优化。默认场景应优先 synchronized,有特殊需求再上 ReentrantLock。
对比表
| 维度 | synchronized | ReentrantLock |
|---|---|---|
| 使用方式 | 关键字,修饰方法或代码块 | lock()/unlock(),finally 中释放 |
| 可重入 | 支持 | 支持 |
| 中断响应 | 不支持(阻塞中不可中断) | lockInterruptibly() 支持 |
| 超时获取 | 不支持 | tryLock(timeout) 支持 |
| 公平性 | 只能非公平 | 默认非公平,可传 true 建公平锁 |
| 条件变量 | 仅 wait/notify 单队列 | newCondition() 可建多个等待队列 |
| 底层实现 | Monitor + 锁升级 | AQS + CAS |
选择建议
- 只需要互斥与重入:用 synchronized,代码更少,还不存在忘记释放的风险。
- 需要“抢不到就放弃、等超时、可被中断”:用 ReentrantLock。
- 需要多个等待条件(如生产者消费者的 notEmpty/notFull):ReentrantLock + Condition。
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.TimeUnit;
public class LockDemo {
private final ReentrantLock lock = new ReentrantLock(true); // 公平锁
public boolean tryOnce(long timeout) throws InterruptedException {
if (lock.tryLock(timeout, TimeUnit.MILLISECONDS)) {
try {
return true; // 拿到锁执行临界区
} finally {
lock.unlock(); // 务必释放,防止“幽灵锁”
}
}
return false; // 超时未拿到锁
}
}
常见追问 / 记忆点
- 记忆点:synchronized 是保底默认,Lock 是“高级选项”,招牌是中断、超时、公平与多条件。
- 追问:Lock 的释放必须放 finally;漏释放会留下锁,轻则性能下降重则死锁。
- 追问:公平锁吞吐略低但能避免线程饥饿;非公平锁吞吐更高,是默认选择。