ReentrantLock类详解
1. 核心概念
ReentrantLock
是Java中基于 java.util.concurrent.locks.Lock
接口实现的可重入互斥锁,提供比 synchronized
更灵活的锁控制机制。关键特性包括:
- 可重入性:同一线程可多次获取同一把锁。
- 公平性选择:支持公平锁(先请求先获取)和非公平锁(默认,允许插队)。
- 可中断锁获取:线程等待锁时可响应中断。
- 超时尝试锁:指定时间内尝试获取锁,避免无限等待。
2. 核心方法与使用
(1) 基础用法
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo {private final ReentrantLock lock = new ReentrantLock();public void safeMethod() {lock.lock(); // 获取锁try {// 临界区代码System.out.println("线程安全操作");} finally {lock.unlock(); // 必须手动释放锁}}
}
- 关键点:
- 必须使用
try-finally
确保锁的释放,防止死锁。 - 锁的获取与释放必须成对出现,否则导致其他线程永久阻塞。
- 必须使用
(2) 高级方法
方法 | 作用 |
---|---|
boolean tryLock() | 尝试非阻塞获取锁,成功返回 true ,失败立即返回 false 。 |
boolean tryLock(long timeout, TimeUnit unit) | 超时等待获取锁,超时后返回 false 。 |
void lockInterruptibly() | 可中断地获取锁,等待过程中可响应中断。 |
boolean isHeldByCurrentThread() | 判断当前线程是否持有锁。 |
int getHoldCount() | 返回当前线程持有该锁的次数(重入次数)。 |
3. 公平锁与非公平锁
(1) 构造函数
// 默认非公平锁(性能更高,允许线程插队)
ReentrantLock nonFairLock = new ReentrantLock(); // 公平锁(按请求顺序分配锁,减少线程饥饿)
ReentrantLock fairLock = new ReentrantLock(true);
(2) 对比
特性 | 非公平锁 | 公平锁 |
---|---|---|
性能 | 高(减少线程切换开销) | 低(需维护等待队列) |
线程饥饿 | 可能发生(新线程可能插队) | 避免线程饥饿 |
适用场景 | 高并发、锁持有时间短 | 严格要求顺序的业务(如订单处理) |
4. 与synchronized的对比
对比项 | ReentrantLock | synchronized |
---|---|---|
锁获取方式 | 显式调用 lock() 和 unlock() | 隐式获取和释放(代码块或方法) |
可中断性 | 支持(lockInterruptibly() ) | 不支持 |
超时机制 | 支持(tryLock(timeout) ) | 不支持 |
公平性 | 可配置 | 仅非公平 |
锁绑定条件 | 支持多个 Condition | 一个对象只能绑定一个等待队列 |
性能 | 高并发下更优 | 优化后接近(低竞争场景更简单) |
5. Condition条件变量
Condition
用于替代 Object.wait()
/notify()
,实现更精准的线程等待与唤醒。
(1) 基本用法
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();public void await() throws InterruptedException {lock.lock();try {condition.await(); // 释放锁并等待} finally {lock.unlock();}
}public void signal() {lock.lock();try {condition.signal(); // 唤醒一个等待线程} finally {lock.unlock();}
}
(2) 多条件示例(生产者-消费者模型)
private final Condition notFull = lock.newCondition(); // 队列未满条件
private final Condition notEmpty = lock.newCondition(); // 队列非空条件// 生产者
public void put(Object item) throws InterruptedException {lock.lock();try {while (queue.isFull()) {notFull.await(); // 等待队列未满}queue.add(item);notEmpty.signal(); // 唤醒消费者} finally {lock.unlock();}
}// 消费者
public Object take() throws InterruptedException {lock.lock();try {while (queue.isEmpty()) {notEmpty.await(); // 等待队列非空}Object item = queue.remove();notFull.signal(); // 唤醒生产者return item;} finally {lock.unlock();}
}
6. 最佳实践与注意事项
- 避免锁泄漏:确保
unlock()
在finally
块中执行。 - 减少锁持有时间:仅在必要代码段加锁,提升并发性能。
- 优先使用非公平锁:除非业务严格要求顺序。
- 合理使用Condition:避免复杂条件逻辑导致代码难以维护。
总结
ReentrantLock
提供了比 synchronized
更精细的锁控制能力,适用于高并发、需要灵活锁管理的场景。通过结合 Condition
和公平性策略,可以实现复杂的线程协作逻辑。然而,其手动管理锁的特性也要求开发者更谨慎地编写代码,避免死锁和资源泄漏。