synchronized和ReentrantLock的区别是什么?各自的使用场景?

在 Java 并发编程中,synchronizedReentrantLock 都是用于保证线程同步的常用机制,但它们在底层实现、功能特性和适用场景上有明显区别。

一、核心区别

特性 synchronized ReentrantLock
实现层级 JVM 内置关键字,通过监视器(Monitor)实现 JDK 提供的 API 级别的锁(java.util.concurrent.locks 包)
锁的获取与释放 自动获取 / 释放(由 JVM 管理,发生异常时 JVM 会自动释放锁) 需手动调用 lock()unlock(),通常在 finally 块中释放,否则可能死锁
可重入性 支持(同一个线程可多次进入同一把锁的同步块) 支持(同一线程可多次调用 lock(),需对应次数 unlock()
可中断获取锁 不支持(线程在等待锁时无法被中断,除非中断后抛出 InterruptedException 的某些情况,但通常不够灵活) 支持 lockInterruptibly(),等待锁的线程可响应中断
超时获取锁 不支持 支持 tryLock(long timeout, TimeUnit unit),超时未获取到锁返回 false
公平锁 非公平锁(JVM 不保证等待时间最长的线程优先获得锁) 默认非公平,但构造时可指定 true 创建公平锁
条件变量 每个对象只有一个等待队列(通过 wait() / notify() / notifyAll() 支持多个 Condition 对象(newCondition()),可精细控制不同条件下的线程等待与唤醒
性能 早期性能较差,经过优化后(偏向锁、轻量级锁等)在大部分场景下与 ReentrantLock 相差不大 性能稳定,但在高度竞争下由于需要 CAS 和队列维护,可能略高于 synchronized(现代 JVM 中差距很小)
调试友好 在线程 dump 中能清晰显示哪些帧持有锁以及等待锁的线程 同样能看到,但需要区分 AbstractQueuedSynchronizer 状态

二、使用场景

1. synchronized 适用场景

  • 简单同步逻辑:只需保护临界区,不需要高级功能(如中断、超时)。
  • 代码简洁优先:利用 JVM 自动管理锁,降低出错概率。
  • 使用 wait/notify 机制:操作简单,一个条件队列足以满足需求。
  • 无锁竞争或低竞争环境:JVM 的偏向锁、轻量级锁优化效果明显。
  • 不易出错的场景:避免手动释放锁可能导致的死锁问题。
java 复制代码
// 示例:synchronized 保护共享变量
public synchronized void increment() {
    counter++;
}

2. ReentrantLock 适用场景

  • 需要可中断的锁获取 :避免死锁时,可用 lockInterruptibly() 让等待线程响应中断。
  • 需要超时获取锁 :使用 tryLock(long timeout, TimeUnit unit) 避免线程永久阻塞。
  • 公平锁需求:某些业务需要按照线程等待顺序获得锁(但公平锁通常降低吞吐量,需权衡)。
  • 多个条件队列:例如生产者-消费者模式,需要分别控制"不满"和"不空"两个条件。
  • 尝试非阻塞获取锁tryLock() 可在获取不到锁时立即返回,做其他事情。
  • 复杂的锁操作:如需要锁降级、锁的跨方法获取与释放(但需格外小心)。
java 复制代码
// 示例:ReentrantLock 带超时和条件变量
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();

public void put(Object item) throws InterruptedException {
    lock.lockInterruptibly();
    try {
        while (isFull()) {
            notFull.await();
        }
        // 添加元素
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
}

三、总结建议

  • 优先使用 synchronized :除非真的需要 ReentrantLock 提供的高级特性,否则 synchronized 更简单、安全、不易出错。现代 JVM 对其优化非常好。
  • 使用 ReentrantLock 当遇到以下情况:需要可中断 / 超时的锁获取、公平锁、多个条件等待集合、尝试锁获取。
  • 注意性能 :在高度竞争的锁场景下,ReentrantLock 通常表现更稳定;但在低竞争或临界区极短时,synchronized 可能由于偏向锁更快。实际开发中应基于需求选择,避免过早优化。
相关推荐
沐言人生3 小时前
ReactNative 源码分析5——ReactActivity之启动RN应用
android·react native
programhelp_4 小时前
Meta SDE 面经分享|VO 四轮高强度输出,系统设计追问非常深
经验分享·面试·职场和发展
嵌入式小企鹅4 小时前
大模型算法工程师面试宝典
人工智能·学习·算法·面试·职场和发展·大模型·面经
__water4 小时前
【关于unity打包Android失败问题】
android·unity
青山师5 小时前
Java反射深度解析:运行时探查的艺术、代价与工程实践
java·开发语言·面试·反射·java程序员·java核心
MonkeyKing71555 小时前
iOS 开发 Block 底层结构、循环引用及解决方案
ios·面试
冻感糕人~5 小时前
大模型面试干货:小白程序员如何准备,轻松拿下高薪Offer?收藏这份独家秘籍!
java·人工智能·学习·ai·面试·职场和发展·大模型学习
重生之小比特5 小时前
【MySQL 数据库】基本查询
android·数据库·mysql
薛定猫AI5 小时前
【技术干货】用 AI + Expo 打通 iOS / Android / Web 跨端应用开发:从架构到代码生成实战
android·人工智能·ios