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 可能由于偏向锁更快。实际开发中应基于需求选择,避免过早优化。
相关推荐
怕浪猫8 小时前
长会话不崩盘:DeepSeek Harness 的上下文压缩与目标管理策略
面试·github·agent
ocean21038 小时前
2025-2026年Python面试高频知识点洞察
开发语言·python·面试·python八股文
一航jason9 小时前
Android平台推理框架及试用场景模型对比
android·人工智能·ai·架构·ai编程·llama
一航jason10 小时前
Android 端侧大模型推理框架对比
android·人工智能·ai·ai编程·llama·ai-native
letisgo510 小时前
JAVA 高级进阶07篇《@Transactional失效的8个场景:代理机制到传播行为》
java·面试·transactional·spring事务·aop动态代理
新时代牛马10 小时前
cyclictest 毛刺从哪来?从ftrace、latencytop、perf到IRQ/调度延迟定位
android·开发语言·python·kotlin
光电的一只菜鸡12 小时前
为什么调整LSC会对AF产生影响
android·开发语言·kotlin
黄敬峰12 小时前
一文搞懂 LangGraph 分支、循环与状态持久化
面试
凛_Lin~~12 小时前
LiveData 源码解析
android·安卓·livedata
hai_android13 小时前
FusibleFlow:Kotlin Flow 的融合机制原理
android·kotlin