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 可能由于偏向锁更快。实际开发中应基于需求选择,避免过早优化。
相关推荐
三少爷的鞋13 分钟前
Android 架构这些年,其实一直在消灭碎片化异步
android
奔跑的蜗牛ing14 小时前
CentOS Nginx 安装与静态文件服务器配置指南
前端·面试·架构
plainGeekDev16 小时前
依赖管理 → Version Catalog
android·java·kotlin
KaifuZeng17 小时前
通信与接口协议面试五、RS422
嵌入式硬件·面试·通信与接口
韩曙亮18 小时前
【Flutter】Flutter 进程保活 ③ ( 屏幕常亮设置 | Flutter 禁止息屏、关闭自动锁屏、屏蔽系统屏保 )
android·flutter·ios·屏幕常亮·禁止息屏
爱笑鱼18 小时前
NDK、SDK、APP、Framework、Native 到底怎么区分?
android
SamDeepThinking19 小时前
组合还是继承?我在项目里主要看这两个判断
java·后端·面试
wear工程师19 小时前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
私人珍藏库19 小时前
[Android] 发票制作器-内置多行业专业模板
android·人工智能·app·软件·多功能
阿pin19 小时前
Android随笔-PMS
android·pms