synchronized
和 Lock
是 Java 中两种不同的锁机制,它们在实现方式、功能和适用场景上有显著区别:
核心区别对比
特性 | synchronized | Lock (如 ReentrantLock) |
---|---|---|
实现层级 | JVM 层面实现(关键字) | JDK 层面实现(接口+实现类) |
锁获取/释放 | 自动获取和释放(进入/退出同步块) | 手动控制(必须显式调用 lock() /unlock() ) |
锁类型 | 非公平锁(默认) | 可选择公平锁或非公平锁 |
锁中断 | ❌ 不支持中断等待 | ✅ 支持 lockInterruptibly() |
尝试获取锁 | ❌ 不支持 | ✅ 支持 tryLock() (带超时/非阻塞) |
条件队列 | 单条件队列(wait() /notify() ) | 多条件队列(通过 Condition 实现) |
性能 | Java 6+ 优化后接近 Lock | 高竞争场景下更优 |
锁状态 | ❌ 无法查询锁状态 | ✅ 可查询(如 isLocked() , hasQueuedThreads() ) |
使用场景分析
1. 优先使用 synchronized
的场景
-
简单同步需求
如单方法内的线程安全控制:public synchronized void transfer(Account target, int amount) { this.balance -= amount; target.balance += amount; }
-
资源竞争不激烈
低并发场景(如内部管理系统),JVM 优化后性能足够。 -
避免锁泄漏
自动释放机制防止忘记解锁:// synchronized 自动释放 synchronized (lockObj) { // 即使抛出异常也会释放锁 doSomething(); }
2. 优先使用 Lock
的场景
-
需要高级锁功能
Lock lock = new ReentrantLock(); if (lock.tryLock(1, TimeUnit.SECONDS)) { // 超时获取 try { // 临界区 } finally { lock.unlock(); } } else { // 处理超时逻辑 }
-
公平锁需求
防止线程饥饿:Lock fairLock = new ReentrantLock(true); // 公平锁
-
细粒度条件控制
生产者-消费者模型:Lock lock = new ReentrantLock(); Condition notEmpty = lock.newCondition(); Condition notFull = lock.newCondition(); // 生产者 lock.lock(); try { while (queue.isFull()) notFull.await(); // 只唤醒生产者 queue.add(item); notEmpty.signal(); } finally { lock.unlock(); }
-
锁中断支持
应对死锁场景:try { lock.lockInterruptibly(); // 可响应中断 // ... } catch (InterruptedException e) { // 处理中断 }
性能对比
场景 | synchronized | Lock |
---|---|---|
低竞争 | ✅ 更优(JVM 优化) | ⚠️ 轻微开销 |
高竞争 | ⚠️ 竞争激烈时性能下降 | ✅ 更稳定(CAS 优化) |
锁升级 | ✅ 无锁→偏向锁→轻量锁→重量锁 | ❌ 直接重量级锁 |
Java 6+ 中
synchronized
通过锁膨胀优化:
无锁 → 偏向锁(单线程) → 轻量级锁(自旋) → 重量级锁(阻塞)
选择原则
- 默认选择
synchronized
满足需求时优先使用(代码简洁、避免人为错误) - 需要以下特性时选
Lock
- 超时获取锁
- 可中断锁
- 公平锁
- 多条件变量
- 锁状态查询
- 高并发场景
根据压测结果选择(Lock
在极高竞争下表现更稳定)
代码示例对比
相同功能的不同实现
使用 synchronized
:
public class Counter {
private int count;
public synchronized void increment() {
count++;
}
}
使用 Lock
:
public class Counter {
private int count;
private final Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock(); // 必须保证释放
}
}
}
⚠️ 关键提示:
Lock
必须配合try-finally
确保解锁,否则可能导致死锁!
🐂🐎
维度 | 结论 |
---|---|
简洁性 | synchronized 胜出(自动管理) |
功能性 | Lock 胜出(支持高级特性) |
性能 | 低竞争:synchronized ≈ Lock 高竞争: Lock 更稳定 |
最佳实践 | 80% 场景用 synchronized ,20% 复杂场景用 Lock |
博主建议:
- 优先尝试
synchronized
,遇到功能限制时再切换到Lock
- 使用
Lock
时务必遵循lock()
→try
→finally { unlock() }
模式
程序员面试资料大全|各种技术书籍等资料-1000G
Git高速下载