Synchronized
和 ReentrantLock
都是 Java 中用于实现线程同步的机制,但它们在实现方式、功能和灵活性上有显著的区别。以下是它们的主要区别:
1. 实现方式
Synchronized
:- 是 Java 的关键字,由 JVM 直接支持。
- 基于 监视器锁(Monitor) 实现,锁的获取和释放由 JVM 自动管理。
- 锁的状态存储在对象头中。
ReentrantLock
:- 是
java.util.concurrent.locks
包中的一个类,基于 API 实现。 - 基于 AQS(AbstractQueuedSynchronizer) 实现,锁的获取和释放需要手动管理。
- 锁的状态由
ReentrantLock
内部维护。
- 是
2. 锁的获取与释放
Synchronized
:- 锁的获取和释放是隐式的,进入同步代码块时自动获取锁,退出时自动释放锁。
- 不需要手动管理锁的释放,避免了锁泄漏的风险。
ReentrantLock
:- 锁的获取和释放是显式的,需要通过
lock()
和unlock()
方法手动控制。 - 必须在
finally
块中释放锁,否则可能导致锁泄漏。
- 锁的获取和释放是显式的,需要通过
3. 灵活性
Synchronized
:- 功能相对简单,不支持中断、超时、公平锁等高级特性。
- 只能以非公平的方式获取锁。
ReentrantLock
:- 提供了更灵活的功能,支持以下特性:
- 可中断锁:通过
lockInterruptibly()
方法获取锁,等待锁的过程中可以响应中断。 - 超时获取锁:通过
tryLock(long timeout, TimeUnit unit)
方法尝试在指定时间内获取锁。 - 公平锁:通过构造函数指定是否为公平锁(默认是非公平锁)。
- 条件变量:通过
newCondition()
方法创建Condition
对象,支持更细粒度的线程通信。
- 可中断锁:通过
- 提供了更灵活的功能,支持以下特性:
4. 性能
Synchronized
:- 在 JDK 1.6 之后,JVM 对
synchronized
进行了大量优化(如偏向锁、轻量级锁、锁消除等),性能已经非常接近ReentrantLock
。 - 在低竞争场景下,性能优于
ReentrantLock
。
- 在 JDK 1.6 之后,JVM 对
ReentrantLock
:- 在高竞争场景下,性能可能优于
synchronized
,尤其是在需要高级功能(如超时、中断)时。 - 由于是 API 实现,需要额外的内存开销。
- 在高竞争场景下,性能可能优于
5. 可重入性
Synchronized
和ReentrantLock
都支持可重入性,即同一个线程可以多次获取同一把锁。
6. 代码示例
Synchronized
示例:
public class SynchronizedExample {
private int count = 0;
public synchronized void increment() {
count++;
}
}
ReentrantLock
示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final Lock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 手动加锁
try {
count++;
} finally {
lock.unlock(); // 手动释放锁
}
}
}
7. 总结
特性 | Synchronized | ReentrantLock |
---|---|---|
实现方式 | JVM 内置关键字 | 基于 AQS 的 API 实现 |
锁的获取与释放 | 自动管理 | 手动管理(需显式调用 lock() 和 unlock() ) |
灵活性 | 功能简单,不支持高级特性 | 支持中断、超时、公平锁、条件变量等高级特性 |
性能 | 低竞争场景下性能较好 | 高竞争场景下性能较好 |
可重入性 | 支持 | 支持 |
- 如果只需要简单的同步功能,推荐使用
synchronized
,因为它更简洁且不易出错。 - 如果需要更灵活的锁控制(如超时、中断、公平锁等),推荐使用
ReentrantLock
。
THE END
暂无评论内容