synchronized vs ReentrantLock 区别
一、核心结论
- synchronized :JVM内置关键字,简单省心,自动加锁释放
- ReentrantLock :JDK实现的类,功能强大灵活,手动加锁释放
二、核心区别对比表
| 维度 | synchronized | ReentrantLock |
|---|---|---|
| 底层实现 | JVM层面,基于Monitor监视器 | JDK层面,基于AQS(AbstractQueuedSynchronizer) |
| 锁类型 | 非公平锁(不可修改) | 默认非公平,可指定为公平锁 |
| 可重入性 | ✅ 支持 | ✅ 支持(实现原理相同) |
| 中断响应 | ❌ 不可中断,只能一直等 | ✅ 可中断等待中的线程 |
| 超时获取锁 | ❌ 不支持 | ✅ 支持tryLock(timeout) |
| 条件变量 | ❌ 仅支持一个wait/notify队列 | ✅ 支持多个Condition条件队列 |
| 锁释放 | 自动释放(代码结束/异常) | 必须手动在finally中释放 |
| 性能(JDK1.6+) | 低竞争时更优 | 高竞争时更稳定 |
| 代码复杂度 | 极低 | 中等 |
三、关键特性图示
公平锁 vs 非公平锁
┌───────────── 公平锁(先来先得) ─────────────┐
│ 线程1 → 线程2 → 线程3 → 线程4 → 依次获取锁 │
└─────────────────────────────────────────────┘
┌───────────── 非公平锁(插队优先) ───────────┐
│ 线程1持有锁 → 线程2、3排队 → 线程4刚好到达 │
│ 线程1释放锁 → 线程4直接插队获取锁 │
└─────────────────────────────────────────────┘
四、代码使用对比
synchronized 示例
java
public class SyncDemo {
private int count = 0;
public synchronized void add() {
count++; // 自动加锁,自动释放
}
}
ReentrantLock 示例
java
public class LockDemo {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void add() {
lock.lock(); // 手动加锁
try {
count++;
} finally {
lock.unlock(); // 必须手动释放!
}
}
}
五、常见误区对照表
| 误区 | 正确结论 |
|---|---|
| ReentrantLock一定比synchronized快 | JDK1.6+后,低竞争时synchronized性能更好 |
| synchronized不是可重入锁 | 两者都是可重入锁,实现原理类似 |
| 公平锁一定比非公平锁好 | 公平锁会降低吞吐量,非公平锁性能更高 |
| ReentrantLock可以替代synchronized | 简单场景优先用synchronized,更安全不易出错 |
六、选型建议
| 场景 | 推荐使用 |
|---|---|
| 简单同步场景,代码越少越好 | ✅ synchronized |
| 需要公平锁、中断、超时获取等高级功能 | ✅ ReentrantLock |
| 高并发高竞争场景 | ✅ ReentrantLock |
| 团队新手多,怕忘记释放锁 | ✅ synchronized |