在 Java 中,synchronized 关键字和 Lock 接口(如 ReentrantLock)都是用于实现线程同步的机制,但它们在设计、功能和灵活性上有显著区别。以下是主要区别的详细对比:
1. 实现机制
2. 功能特性
| 特性 | synchronized | Lock |
|---|
| 公平锁 | 仅支持非公平锁 | 支持公平锁(new ReentrantLock(true)) |
| 尝试非阻塞获取锁 | 不支持 | tryLock() 立即返回成功/失败 |
| 超时获取锁 | 不支持 | tryLock(long time, TimeUnit unit) |
| 可中断等待锁 | 不支持 | lockInterruptibly() |
| 绑定多个条件变量 | 不支持 | 支持多个 Condition 队列 |
3. 条件等待(Condition)
4. 性能
- Java 5 之前:
synchronized 性能较差(重量级锁)。 - Java 6+:JVM 对
synchronized 进行了大量优化(偏向锁、轻量级锁、自旋锁等),性能接近 Lock。 - 高竞争场景:
Lock 可能仍优于 synchronized(如利用 tryLock 避免死锁)。
5. 锁的可重入性
- 两者均支持:同一个线程可以重复获取同一把锁(避免自死锁)。
6. 异常处理
synchronized:自动释放锁,无资源泄漏风险。Lock:需在 finally 中调用 unlock(),否则锁可能无法释放(导致死锁)。
7. 适用场景
- 优先使用
synchronized:
简单同步需求,代码简洁性优先(JVM 自动管理)。 - 选择
Lock:
需要高级功能(如公平锁、超时锁、可中断锁、多条件变量)或高竞争场景。
总结对比表
| 维度 | synchronized | Lock |
|---|
| 锁获取方式 | 隐式(自动获取/释放) | 显式(手动 lock()/unlock()) |
| 灵活性 | 较低 | 高(支持多种锁获取策略) |
| 公平锁 | 不支持 | 支持 |
| 条件变量 | 单一等待队列 | 多 Condition 队列 |
| 超时锁 | 不支持 | 支持 |
| 可中断锁 | 阻塞时不可中断 | 支持 |
| 代码复杂度 | 简单(内置于语法) | 需手动管理(易漏 unlock()) |
| 性能趋势 | Java 6+ 优化后接近 Lock | 高竞争场景可能更优 |
代码示例对比
使用 synchronized:
public class SyncExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
使用 Lock:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
}
结论
- 默认选择
synchronized:满足基础同步需求时,优先使用(简洁安全)。 - 需要高级功能时用
Lock:如公平性、超时控制、可中断锁、多条件变量等场景。
务必在 finally 中释放锁,避免死锁!