Synchronized
和 ReentrantLock
都是 Java 中用于实现线程同步的机制,它们在功能上有相似之处,但也有一些关键的区别。以下是它们的主要区别:
1. 底层实现机制
Synchronized
:是 Java 的内置关键字,JVM 直接支持。Synchronized
是一种 非公平锁,使用的是重量级的操作,主要通过对象的监视器锁(Monitor)来实现,依赖于 JVM 层面。ReentrantLock
:是 Javajava.util.concurrent.locks
包中的类,它是基于Java 代码实现的可重入锁,提供了更灵活的锁机制。它属于显式锁,即开发者需要手动加锁和解锁。
2. 公平锁与非公平锁
Synchronized
:只能是 非公平锁,即无法保证线程获取锁的顺序。线程争抢锁时,不能保证最早等待的线程一定能优先获取到锁。ReentrantLock
:可以选择是 公平锁 或 非公平锁。通过构造函数ReentrantLock(boolean fair)
,可以指定是否创建公平锁。如果使用公平锁,等待时间最长的线程优先获取锁。
3. 锁的获取与释放
Synchronized
:锁的获取与释放是隐式的。线程进入synchronized
块或方法时自动获取锁,退出时自动释放锁,即便线程异常退出也能保证锁会被释放。ReentrantLock
:锁的获取与释放是显式的,必须使用lock()
手动加锁,使用unlock()
手动解锁。如果在try-finally
代码块中没有正确释放锁,可能导致死锁问题,因此通常建议在finally
块中释放锁。
示例:
ReentrantLock lock = new ReentrantLock();
try {
lock.lock(); // 显式加锁
// 线程安全的代码块
} finally {
lock.unlock(); // 显式解锁
}
4. 中断响应
Synchronized
:不支持中断锁等待。如果一个线程在等待锁时被阻塞,它无法响应中断信号。ReentrantLock
:支持中断。通过lockInterruptibly()
方法,线程在等待获取锁时可以响应中断,从而避免长时间等待或死锁的情况。
示例:
try {
lock.lockInterruptibly(); // 支持中断的锁
} catch (InterruptedException e) {
// 响应中断
}
5. 尝试获取锁
Synchronized
:不支持尝试获取锁,线程要么成功获取锁,要么一直等待直到锁释放。ReentrantLock
:可以使用tryLock()
方法尝试获取锁,立即返回锁是否成功获取。如果未成功获取锁,线程不会阻塞,还可以设定超时时间,超时后停止等待。
示例:
if (lock.tryLock()) {
try {
// 成功获取锁后执行
} finally {
lock.unlock();
}
} else {
// 没有成功获取锁
}
6. 条件变量(Condition
)
Synchronized
:依赖于Object
类的wait()
和notify()
/notifyAll()
方法实现线程的等待和通知机制,但每个锁只有一个等待队列。ReentrantLock
:支持多个条件变量,可以通过newCondition()
方法创建多个Condition
对象,以实现更加精细化的线程等待与通知机制。
示例:
Condition condition = lock.newCondition();
try {
lock.lock();
condition.await(); // 等待
condition.signal(); // 唤醒
} finally {
lock.unlock();
}
7. 性能与可扩展性
Synchronized
:在 Java 1.6 及以上版本,经过多次优化(如自适应自旋锁、轻量级锁、偏向锁等),在大部分情况下性能表现较好,适合简单的同步场景。ReentrantLock
:性能稍高一些,尤其在高并发环境下,它提供了更多的控制能力,能够适应复杂的并发场景。
8. 可重入性
Synchronized
和ReentrantLock
都是可重入锁。也就是说,线程在持有锁的情况下可以多次获取该锁,而不会发生死锁。例如,同一个线程可以多次进入被synchronized
修饰的代码块或调用多次lock()
。
9. 锁的降级
Synchronized
:不支持锁降级。ReentrantLock
:支持锁降级,即在持有写锁的情况下,可以降级为读锁(通过ReadWriteLock
实现),以提升性能。
总结
特性 | Synchronized | ReentrantLock |
---|---|---|
锁的获取与释放 | 隐式 | 显式 |
公平锁 | 不支持 | 支持公平与非公平两种模式 |
中断响应 | 不支持 | 支持 lockInterruptibly() |
尝试获取锁 | 不支持 | 支持 tryLock() |
多条件变量 | 不支持 | 支持多个 Condition |
性能 | JVM 优化后性能较好 | 提供更多功能和更灵活的控制 |
使用场景 | 适用于简单的同步场景 | 适用于复杂的并发控制场景 |
Synchronized
适合简单的同步需求,因为它的使用更为简洁且可以自动处理锁的释放。ReentrantLock
则提供了更强的控制能力,适合需要高级功能(如公平锁、中断、多个条件变量)的场景。