首页 > 项目 > 当前页面

synchronized 和 ReentrantLock 的区别详解

2026-06-13 NEW个对象

📌 synchronized 和 ReentrantLock 的区别详解

核心结论: synchronized 是 JVM 层面提供的内置锁,而 ReentrantLock 是 JDK 层面基于 AQS(AbstractQueuedSynchronizer)实现的可重入锁。两者都能保证线程安全,但 ReentrantLock 提供了更丰富的功能,如公平锁、可中断锁、超时获取锁、多条件队列等。

1️⃣ 问题背景

在多线程环境下,多个线程同时访问共享资源时,会产生线程安全问题。

例如:

  • 多个线程同时修改库存
  • 多个线程同时更新账户余额
  • 多个线程同时写入缓存
  • 多个线程同时生成订单号

如果不进行同步控制,就会导致数据不一致、脏数据、超卖等问题。

Java 提供了两种最常见的同步机制:

  • synchronized
  • ReentrantLock

面试中经常会问:

为什么已经有 synchronized 了,还要设计 ReentrantLock?

答案是:

synchronized 能解决线程安全问题,但功能相对简单;ReentrantLock 在此基础上扩展了更多高级能力。


2️⃣ 核心原理

synchronized 原理

synchronized 是 JVM 提供的关键字。

每个 Java 对象都关联一个 Monitor(监视器锁)。

线程进入 synchronized 代码块时:

Monitor Enter
↓
获得对象锁
↓
执行临界区代码
↓
Monitor Exit
↓
释放对象锁

底层依赖 JVM 的 monitorenter 和 monitorexit 指令实现。

ReentrantLock 原理

ReentrantLock 位于:

java.util.concurrent.locks

底层基于 AQS(AbstractQueuedSynchronizer)实现。

AQS 维护:

  • state 状态值
  • CLH 双向队列
  • CAS 自旋操作
  • LockSupport park/unpark

获取锁时通过 CAS 修改 state。

获取失败则进入同步队列等待。


3️⃣ 数据结构分析

synchronized

对象头(Mark Word)中保存锁信息。

Object Header
├── Mark Word
├── Klass Pointer

Mark Word 会记录:

  • 偏向锁信息
  • 轻量级锁信息
  • 重量级锁信息
  • 线程ID
  • 锁标志位

ReentrantLock

核心结构:

ReentrantLock
↓
Sync
↓
AQS
↓
state
↓
CLH Queue

其中:

  • state = 0 表示无锁
  • state > 0 表示已经被占用
  • state 值表示重入次数

4️⃣ 算法分析

synchronized 算法

JDK1.6 之后进行了大量优化:

  • 偏向锁
  • 轻量级锁
  • 自旋锁
  • 重量级锁

锁会根据竞争情况自动升级。

无锁
↓
偏向锁
↓
轻量级锁
↓
重量级锁

锁只能升级,不能降级。

ReentrantLock 算法

核心流程:

CAS 抢锁
↓
成功
↓
获得锁

失败:

CAS失败
↓
加入CLH队列
↓
阻塞等待
↓
被唤醒继续竞争

5️⃣ 执行流程

synchronized 使用方式

public synchronized void add() {
  count++;
}

JVM 自动完成:

  • 加锁
  • 释放锁
  • 异常处理

ReentrantLock 使用方式

private ReentrantLock lock = new ReentrantLock();

public void add() {
  lock.lock();
  try {
    count++;
  } finally {
    lock.unlock();
  }
}

必须手动释放锁。

否则可能发生死锁。


6️⃣ 实际案例

案例一:公平锁

ReentrantLock lock = new ReentrantLock(true);

true 表示公平锁。

线程按照进入队列顺序获取锁。

synchronized 无法实现公平锁。

案例二:超时等待锁

if(lock.tryLock(3, TimeUnit.SECONDS)){
  //业务逻辑
}

等待 3 秒仍未获取锁则放弃。

避免线程永久阻塞。

案例三:可中断锁

lock.lockInterruptibly();

线程等待锁期间可响应 interrupt。

synchronized 无法实现。


7️⃣ 优缺点分析

对比项 synchronized ReentrantLock
实现层面 JVM JDK(AQS)
自动释放锁 支持 不支持
公平锁 不支持 支持
可中断 不支持 支持
超时获取锁 不支持 支持
Condition条件队列 不支持 支持
易用性 高 一般
性能 JDK1.6后接近 JDK1.6后接近

synchronized 优点

  • 语法简单
  • 自动释放锁
  • 不容易写出死锁代码
  • JVM优化成熟

ReentrantLock 优点

  • 支持公平锁
  • 支持超时机制
  • 支持可中断获取锁
  • 支持多个 Condition
  • 扩展能力强

8️⃣ 面试常见问题

1. synchronized 和 ReentrantLock 哪个性能更高?

JDK1.6 之后 synchronized 已经过大量优化,两者性能差距已经很小。

2. ReentrantLock 为什么叫可重入锁?

同一个线程已经持有锁后,可以再次获取该锁,不会发生死锁。

3. ReentrantLock 底层是什么实现的?

AQS + CAS + CLH 双向队列。

4. synchronized 为什么也是可重入锁?

Monitor 中记录持有锁线程和重入次数,同一个线程再次进入同步代码块时只增加计数即可。

5. 为什么 ReentrantLock 必须放 finally?

防止业务异常导致 unlock 未执行,从而产生死锁。

面试标准回答:
如果只需要简单同步,优先使用 synchronized;如果需要公平锁、超时锁、可中断锁、多个条件队列等高级功能,则使用 ReentrantLock。

9️⃣ 总结

本质区别:

① synchronized 是 JVM 内置锁,底层基于 Monitor。

② ReentrantLock 是 JDK 实现锁,底层基于 AQS。

③ synchronized 自动加锁、自动释放锁。

④ ReentrantLock 需要手动 lock/unlock。

⑤ ReentrantLock 提供公平锁、超时锁、可中断锁等高级能力。

⑥ JDK1.6 后 synchronized 经过偏向锁、轻量级锁、自旋锁等优化,性能已经非常接近 ReentrantLock。

实际开发经验:

业务开发优先 synchronized;框架、中间件、高并发组件开发优先 ReentrantLock。

相关文章

  • Mysql一条sql的写入流程?

    MySQL 在执行写操作时,会先修改 Buffer Pool,并记录 undo log 用于回滚,同时写 redo log(prepare),再写 binlog,最后通过两阶段提交保证 redo log 和 binlog 一致性,从而实现 crash-safe 和主从一致性

    NEW个对象 2026-06-06

  • B树与B+树的区别

    B树与B+树的区别

    NEW个对象 2024-12-25

  • Spring如何通过三级缓存解决循环依赖问题

    三级缓存: 一级缓存(singletonObjects):存储已经完全初始化的单例 Bean 对象。 二级缓存(earlySingletonObjects):存储已经实例化但尚未完全初始化的单例 Bean 对象。 三级缓存(singletonFactories):存储 Bean 对象的创建工厂,用于在创建过程中检测循环依赖。

    NEW个对象 2024-12-17

NEW个对象 NEW个对象
JAVA是世界上最好的语言