问题
ReentrantLock增加了轮询、超时、中断、公平锁和非公平锁等高级功能。
答案
1. 核心概念
相比 synchronized,ReentrantLock 提供了以下高级功能:
- 轮询(tryLock):非阻塞尝试获取锁
- 超时(tryLock with timeout):在指定时间内尝试获取锁
- 可中断(lockInterruptibly):等待锁时可响应中断
- 公平锁(Fair Lock):按照线程请求顺序分配锁
- 非公平锁(Unfair Lock):允许”插队”,性能更好
- 多条件变量(Condition):支持多个等待队列
这些高级功能使 ReentrantLock 在复杂并发场景下比 synchronized 更灵活强大。
2. 轮询功能(tryLock)
2.1 基本概念
tryLock():非阻塞地尝试获取锁,立即返回成功或失败。
特点:
- 不会阻塞线程
- 立即返回
true(成功)或false(失败) - 可以避免死锁
2.2 使用示例
ReentrantLock lock = new ReentrantLock();
// 非阻塞尝试获取锁
if (lock.tryLock()) {
try {
// 成功获取锁,执行业务逻辑
System.out.println("获取锁成功");
} finally {
lock.unlock();
}
} else {
// 获取锁失败,执行其他逻辑
System.out.println("获取锁失败,执行其他任务");
}
2.3 避免死锁
public class DeadlockAvoidance {
private final ReentrantLock lock1 = new ReentrantLock();
private final ReentrantLock lock2 = new ReentrantLock();
public void transferMoney() {
while (true) {
if (lock1.tryLock()) { // 尝试获取 lock1
try {
if (lock2.tryLock()) { // 尝试获取 lock2
try {
// 两个锁都获取成功,执行转账
System.out.println("转账成功");
return;
} finally {
lock2.unlock();
}
}
} finally {
lock1.unlock();
}
}
// 如果获取失败,稍后重试
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
3. 超时功能(tryLock with timeout)
3.1 基本概念
tryLock(long timeout, TimeUnit unit):在指定时间内尝试获取锁,超时返回失败。
特点:
- 限制等待时间,避免无限阻塞
- 可以设置超时策略
- 超时后可执行补偿逻辑
3.2 使用示例
ReentrantLock lock = new ReentrantLock();
// 在 3 秒内尝试获取锁
try {
if (lock.tryLock(3, TimeUnit.SECONDS)) {
try {
// 成功获取锁
System.out.println("获取锁成功");
} finally {
lock.unlock();
}
} else {
// 超时未获取到锁
System.out.println("获取锁超时");
}
} catch (InterruptedException e) {
System.out.println("等待锁时被中断");
}
3.3 实际应用:防止饥饿
public class OrderService {
private final ReentrantLock lock = new ReentrantLock();
public boolean processOrder(Order order) {
try {
// 最多等待 5 秒
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
// 处理订单
return true;
} finally {
lock.unlock();
}
} else {
// 超时,订单处理失败
System.err.println("订单处理超时,订单号:" + order.getId());
return false;
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return false;
}
}
}
4. 可中断功能(lockInterruptibly)
4.1 基本概念
lockInterruptibly():在等待锁的过程中,可以响应中断信号。
特点:
- 可以取消正在等待的线程
- 抛出
InterruptedException - 比 synchronized 更灵活
对比 synchronized:
synchronized:等待锁时不可中断ReentrantLock:通过lockInterruptibly()可中断
4.2 使用示例
ReentrantLock lock = new ReentrantLock();
Thread t = new Thread(() -> {
try {
System.out.println("尝试获取锁");
lock.lockInterruptibly(); // 可中断等待
try {
System.out.println("获取锁成功");
Thread.sleep(10000); // 模拟长时间持有锁
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("等待锁时被中断");
}
});
t.start();
Thread.sleep(1000);
// 中断线程
t.interrupt();
System.out.println("发送中断信号");
// 输出:
// 尝试获取锁
// 发送中断信号
// 等待锁时被中断
4.3 实际应用:任务取消
public class CancelableTask {
private final ReentrantLock lock = new ReentrantLock();
public void executeTask() {
try {
lock.lockInterruptibly(); // 可中断
try {
// 执行长时间任务
while (!Thread.currentThread().isInterrupted()) {
// 业务逻辑
}
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
System.out.println("任务被取消");
Thread.currentThread().interrupt(); // 恢复中断状态
}
}
public void cancelTask(Thread taskThread) {
taskThread.interrupt(); // 中断任务线程
}
}
5. 公平锁功能
5.1 基本概念
公平锁(Fair Lock):按照线程请求锁的顺序(FIFO)分配锁。
特点:
- 保证先来先得
- 避免线程饥饿
- 性能略低于非公平锁
5.2 使用示例
// 创建公平锁
ReentrantLock fairLock = new ReentrantLock(true);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
fairLock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获得锁");
} finally {
fairLock.unlock();
}
}, "Thread-" + i).start();
}
// 输出(按顺序):
// Thread-0 获得锁
// Thread-1 获得锁
// Thread-2 获得锁
// Thread-3 获得锁
// Thread-4 获得锁
5.3 实际应用:订单处理系统
public class OrderProcessor {
// 公平锁保证订单按时间顺序处理
private final ReentrantLock lock = new ReentrantLock(true);
public void processOrder(Order order) {
lock.lock();
try {
System.out.println("处理订单:" + order.getId() + ",时间:" + order.getTimestamp());
// 处理订单逻辑
} finally {
lock.unlock();
}
}
}
6. 非公平锁功能
6.1 基本概念
非公平锁(Unfair Lock):线程获取锁时不考虑等待队列,直接尝试获取。
特点:
- 允许”插队”
- 性能更好(减少上下文切换)
- 可能导致线程饥饿
6.2 使用示例
// 创建非公平锁(默认)
ReentrantLock unfairLock = new ReentrantLock();
// 或显式指定
ReentrantLock unfairLock = new ReentrantLock(false);
unfairLock.lock();
try {
// 业务逻辑
} finally {
unfairLock.unlock();
}
7. 多条件变量(Condition)
7.1 基本概念
Condition:类似于 Object.wait()/notify(),但更强大。
特点:
- 一个锁可以创建多个 Condition
- 精准控制线程的等待和唤醒
- 支持
await()、signal()、signalAll()
7.2 使用示例
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition(); // 条件 1:不满
Condition notEmpty = lock.newCondition(); // 条件 2:不空
// 生产者
lock.lock();
try {
while (queue.isFull()) {
notFull.await(); // 等待"不满"条件
}
queue.add(item);
notEmpty.signal(); // 通知"不空"条件
} finally {
lock.unlock();
}
// 消费者
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待"不空"条件
}
Item item = queue.remove();
notFull.signal(); // 通知"不满"条件
} finally {
lock.unlock();
}
7.3 实际应用:阻塞队列
public class BoundedQueue<T> {
private final Queue<T> queue = new LinkedList<>();
private final int capacity;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public BoundedQueue(int capacity) {
this.capacity = capacity;
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 队列满,等待
}
queue.add(item);
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空,等待
}
T item = queue.remove();
notFull.signal(); // 通知生产者
return item;
} finally {
lock.unlock();
}
}
}
8. 功能对比总结
| 功能 | synchronized | ReentrantLock |
|---|---|---|
| 轮询 | ❌ 不支持 | ✅ tryLock() |
| 超时 | ❌ 不支持 | ✅ tryLock(timeout, unit) |
| 可中断 | ❌ 不可中断 | ✅ lockInterruptibly() |
| 公平锁 | ❌ 非公平 | ✅ new ReentrantLock(true) |
| 非公平锁 | ✅ 默认非公平 | ✅ new ReentrantLock(false) |
| 多条件变量 | ❌ 只有一个 | ✅ newCondition() |
9. 选择建议
使用 synchronized:
- 简单场景
- 不需要高级功能
- JVM 内置优化已足够
使用 ReentrantLock:
- 需要轮询或超时获取锁
- 需要可中断的锁等待
- 需要公平锁
- 需要多个条件变量
- 需要获取锁的状态信息(
isLocked()、getHoldCount()等)
10. 实际案例:数据库连接池
public class ConnectionPool {
private final Queue<Connection> connections = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
// 获取连接(超时)
public Connection getConnection(long timeout, TimeUnit unit) throws InterruptedException {
lock.lock();
try {
long nanos = unit.toNanos(timeout);
while (connections.isEmpty()) {
if (nanos <= 0) {
return null; // 超时
}
nanos = notEmpty.awaitNanos(nanos); // 等待指定时间
}
return connections.remove();
} finally {
lock.unlock();
}
}
// 释放连接
public void releaseConnection(Connection conn) {
lock.lock();
try {
connections.add(conn);
notEmpty.signal(); // 通知等待的线程
} finally {
lock.unlock();
}
}
}
11. 性能考量
非公平锁 vs 公平锁:
- 非公平锁性能通常是公平锁的 10-100 倍
- 默认使用非公平锁
可中断 vs 不可中断:
lockInterruptibly()比lock()略慢,但更灵活- 根据需求选择
12. 总结
ReentrantLock 提供的高级功能:
- 轮询(tryLock):非阻塞尝试获取锁,避免死锁
- 超时(tryLock with timeout):限制等待时间,防止无限阻塞
- 可中断(lockInterruptibly):等待锁时可响应中断,支持任务取消
- 公平锁:按顺序分配锁,保证公平性,避免饥饿
- 非公平锁:允许插队,性能更好(默认)
- 多条件变量(Condition):支持多个等待队列,精准控制
面试要点:
- 列举 ReentrantLock 的高级功能
- 说明每个功能的应用场景(轮询避免死锁、超时防止饥饿、可中断支持取消)
- 对比 synchronized 和 ReentrantLock 的差异
- 理解公平锁和非公平锁的性能权衡
记忆口诀:
- 轮询超时可中断,公平非公平可选
- 多个条件精准控,灵活强大比 sync 全