Synchronized 与 ReentrantLock
Java 并发:synchronized 与 ReentrantLock 全面解析
一、前言
在 Java 多线程编程中,线程安全是核心问题。synchronized 与 ReentrantLock 是最常用的两种同步方案,都能实现可重入独占锁,保证临界区代码的原子性、可见性与有序性。
本文从底层原理、使用方式、核心特性、对比选型四个维度,帮你彻底搞懂二者的区别与适用场景。
二、synchronized 详解
2.1 基本介绍
synchronized 是 JVM 内置的隐式锁,由 JVM 自动管理加锁与释放,无需手动编码,使用简单、安全。
2.2 底层原理
- 字节码:通过
monitorenter/monitorexit指令实现 - 运行时:依赖对象头 Mark Word 与 Monitor(监视器锁)
- JDK 1.6+ 优化:偏向锁 → 轻量级锁 → 重量级锁 自动升级
2.3 三种使用方式
// 1. 修饰实例方法(锁当前对象 this)
public synchronized void method() {
// 临界区
}
// 2. 修饰静态方法(锁当前类 Class 对象)
public static synchronized void staticMethod() {
// 临界区
}
// 3. 同步代码块(自定义锁对象,粒度更细)
public void blockMethod() {
synchronized (this) {
// 临界区
}
}
2.4 核心特点
- 自动加锁、自动释放(正常结束/异常都会释放)
- 仅支持非公平锁,无法配置
- 不支持响应中断、不支持超时获取
- 语法简洁,不易出错,适合简单同步场景
三、ReentrantLock 详解
3.1 基本介绍
ReentrantLock 是 JUC 包提供的显式锁,基于 AQS(AbstractQueuedSynchronizer) 实现,功能更强大、可控性更高。
3.2 标准使用模板
必须配合 try-finally 确保锁释放,避免死锁
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock();
public void work() {
// 加锁
lock.lock();
try {
// 临界区业务逻辑
} finally {
// 必须在 finally 释放锁
lock.unlock();
}
}
}
3.3 高级特性(核心优势)
- 支持公平/非公平锁
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁(FIFO)
ReentrantLock unfairLock = new ReentrantLock(); // 非公平锁(默认)
- 支持响应中断
lock.lockInterruptibly(); // 等待锁时可被中断,避免永久阻塞
- 支持超时获取锁
boolean tryLock = lock.tryLock(1, TimeUnit.SECONDS);
- 可绑定多个 Condition 实现精准唤醒
四、核心对比(面试高频)
| 对比项 | synchronized | ReentrantLock |
|---|---|---|
| 实现层面 | JVM 原生实现 | Java 代码基于 AQS 实现 |
| 锁管理 | 隐式,自动加释放 | 显式,手动 lock/unlock |
| 公平性 | 仅非公平 | 支持公平/非公平切换 |
| 中断支持 | 不支持 | 支持 lockInterruptibly |
| 超时获取 | 不支持 | 支持 tryLock(timeout) |
| Condition | 不支持,只能 wait/notify | 支持多 Condition 精准唤醒 |
| 使用成本 | 极简,不易出错 | 需手动释放,易遗漏 |
| 性能 | JDK 1.8+ 优化后接近 | 高并发下更可控 |
五、如何选型(实用建议)
- 优先使用 synchronized
- 简单同步场景、代码量少
- 无需复杂特性,追求安全与简洁
- JVM 会持续优化,维护成本更低
- 选择 ReentrantLock
- 需要公平锁避免线程饥饿
- 需要中断、超时防止死锁
- 需要多 Condition 实现精细化线程调度
六、常见避坑
- ReentrantLock 必须在 finally 里 unlock,否则极易造成锁泄漏
- 锁对象尽量用
private final修饰,避免被外部修改 - 不要在循环中频繁加锁释放,影响性能
- 锁粒度尽可能小,只包裹临界区
七、总结
synchronized:简单、安全、省心,适合绝大多数常规场景ReentrantLock:灵活、强大、可控,适合复杂并发调度
日常开发优先 synchronized;遇到高级并发需求,再用 ReentrantLock 针对性解决。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 Bird的个人博客!





