ReentrantLock
是 Java 中的一个可重入锁,它比 synchronized
关键字提供了更丰富的特性和灵活性。以下是 ReentrantLock
相对于 synchronized
的一些优势:
公平性:
ReentrantLock
允许你选择锁的公平性。如果设置为公平锁,那么等待时间最长的线程将会获取到锁,这有助于避免线程饥饿。而synchronized
锁默认是不公平的,它不保证等待时间最长的线程能够最先获取到锁。
可中断性:
- 使用
ReentrantLock
的lockInterruptibly()
方法,线程在尝试获取锁的过程中可以响应中断,即如果线程在等待获取锁时被中断,它会抛出InterruptedException
并立即返回,而不是一直等待锁释放。
- 使用
超时:
ReentrantLock
提供了tryLock(long timeout, TimeUnit unit)
方法,允许线程尝试在给定的时间内获取锁。如果在指定的时间内锁没有被获取到,线程可以放弃等待,继续执行其他任务,而不是无限期地等待锁的释放。
非阻塞获取:
ReentrantLock
的tryLock()
方法允许线程尝试非阻塞地获取锁。如果锁不可用,线程可以立即返回,而不必浪费CPU时间进行等待。
多个条件变量:
- 与
synchronized
关键字配合wait()
和notify()
/notifyAll()
使用不同,ReentrantLock
可以与Condition
对象配合使用,允许线程等待特定的条件成立。这意味着你可以有多个条件队列,线程可以等待不同的条件,而synchronized
锁只有一个条件队列。
- 与
实现锁的复合操作:
- 通过
ReentrantLock
,你可以在获取、释放锁的同时执行多个操作,这些操作可以作为一个原子操作。而synchronized
则需要通过额外的方式(如使用原子类)来实现复合操作。
- 通过
使用示例:
ReentrantLock lock = new ReentrantLock();
public void performAction() {
lock.lock(); // 获取锁
try {
// 执行受保护的操作
} finally {
lock.unlock(); // 确保释放锁
}
}
// 可中断的锁获取
lock.lockInterruptibly();
try {
// 执行操作
} catch (InterruptedException e) {
// 处理中断
} finally {
lock.unlock();
}
// 超时获取锁
if (lock.tryLock(100, TimeUnit.MILLISECONDS)) {
try {
// 执行操作
} finally {
lock.unlock();
}
} else {
// 处理超时情况
}
// 使用条件对象
Condition condition = lock.newCondition();
lock.lock();
try {
while (!conditionToBeMet()) {
condition.await(); // 等待条件成立
}
// 执行操作
} catch (InterruptedException e) {
// 处理中断
} finally {
lock.unlock();
}
// 信号通知
lock.lock();
try {
// 改变条件状态
condition.signal(); // 唤醒等待的线程
} finally {
lock.unlock();
}
注意事项:
- 使用
ReentrantLock
时,务必在finally
块中释放锁,以避免死锁。 - 相比于
synchronized
,ReentrantLock
需要显式地管理锁的获取和释放,这在一定程度上增加了编码的复杂性。 - 在某些情况下,
synchronized
的性能可能优于ReentrantLock
,因为它是一个更轻量级和更内联的操作。
总的来说,ReentrantLock
提供了比 synchronized
更高级的特性,适用于需要这些特性的复杂同步场景。然而,对于简单的同步需求,synchronized
仍然是一个简洁且有效的选择。