首页 > 项目 > 当前页面

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。

相关文章

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