绝大多数简单同步场景应优先使用 synchronized:它自动加锁解锁、无内存泄漏风险、JVM 优化成熟(如锁消除、偏向锁)、代码简洁;仅在需 tryLock、中断响应、公平锁、多 Condition 或锁状态监控时才用 ReentrantLock。

什么时候该用 synchronized 而不是 ReentrantLock
绝大多数简单同步场景,synchronized 是更优选择:它自动加锁/解锁、无内存泄漏风险、JVM 层面优化成熟(如锁消除、偏向锁、轻量级锁),且代码更简洁。
常见适用场景包括:
- 临界区逻辑短、不涉及条件等待(即不用
wait()/notify()) - 只需要独占访问,不需尝试获取锁(
tryLock())、超时获取(tryLock(long, TimeUnit))或中断响应 - 锁的粒度是方法或代码块,且持有者明确(非跨方法传递锁对象)
- 不需要绑定多个
Condition实现精细的线程协作
ReentrantLock 真正不可替代的使用场景
当需要 synchronized 无法提供的控制能力时,才应切换到 ReentrantLock。这不是“更高级”,而是“更灵活但更易出错”。
典型刚需包括:
立即学习“Java免费学习笔记(深入)”;
- 需要在指定时间内尝试获取锁,避免无限阻塞(例如:接口调用有超时要求,不能卡在锁上)
- 必须响应中断(
lockInterruptibly()),比如任务被Thread.interrupt()中止时能及时退出等待 - 要实现非公平锁之外的调度策略(如构造时传
false启用公平锁) - 需为同一把锁关联多个等待队列(通过
lock.newCondition()创建多个Condition) - 需要在运行时查询锁状态(如
isLocked()、getHoldCount()、hasQueuedThreads())用于监控或调试
最容易踩的坑:忘记在 finally 块中释放 ReentrantLock
synchronized 的释放由 JVM 保证,而 ReentrantLock 必须手动调用 unlock() —— 漏掉就会导致死锁或资源耗尽,且这种错误不会抛异常,极难排查。
Easily find JSON paths within JSON objects using our intuitive Json Path Finder
正确写法只有这一种安全模式:
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock(); // 必须放在这里,哪怕抛异常也要执行
}
常见错误包括:
- 把
unlock()放在try块里(异常时跳过) - 在
catch里释放,但没覆盖所有异常分支 - 用
if (lock.isHeldByCurrentThread())判断再释放(多余且不可靠,unlock()本身已做检查)
性能差异没你想的那么大,别盲目替换
在 JDK 8+ 和 HotSpot 默认配置下,synchronized 在多数场景下性能不输 ReentrantLock,甚至更好——尤其在低竞争、短临界区、锁逃逸可被 JIT 消除的情况下。
真正影响性能的是锁设计本身,而非关键字或类的选择:
- 锁粒度太粗(比如整个方法加锁)比用哪个锁都致命
- 临界区内做 I/O、远程调用、复杂计算,会放大锁等待时间
- 频繁创建新锁对象(如每次方法调用 new 一个
ReentrantLock)反而引发 GC 压力
如果真遇到同步瓶颈,优先考虑无锁结构(AtomicInteger、ConcurrentHashMap)、分段锁、或重构为异步/消息驱动,而不是直接换锁实现。









