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 可能由于偏向锁更快。实际开发中应基于需求选择,避免过早优化。
相关推荐
better_liang3 小时前
每日Java面试场景题知识点之-消息队列MQ核心场景与实战
java·面试·kafka·消息队列·rabbitmq·rocketmq·mq
小江的记录本3 小时前
【JVM虚拟机】垃圾回收GC:四种引用类型:强引用、软引用、弱引用、虚引用(附《思维导图》+《面试高频考点清单》)
java·jvm·spring boot·后端·python·spring·面试
巴博尔4 小时前
UNIAPP中NVUE页面 动画
android·前端·javascript·ios·uni-app
better_liang4 小时前
每日Java面试场景题知识点之-SpringBoot启动流程
java·面试·springboot·源码解析·启动流程
Raink老师4 小时前
【AI面试临阵磨枪-69】如何设计一个支持百万级工具的 Agent 系统?如何快速路由与选择工具?
人工智能·面试·职场和发展
Raink老师5 小时前
【AI面试临阵磨枪-77】音视频 + AI:实时字幕、翻译、降噪、虚拟人、多模态对话
人工智能·面试·音视频
Raink老师8 小时前
【AI面试临阵磨枪-76】社交 AI:内容生成、审核、智能回复、多模态理解、安全治理
人工智能·安全·面试
abc_ABC123A8 小时前
flutter开发安卓APP所需搭建的环境
android
仙俊红8 小时前
Integer\int对比,equals()\hashcode面试
java·面试·职场和发展
kyriewen9 小时前
手写虚拟DOM后,我反问面试官:key为什么不能用index?
前端·react.js·面试