Java ReentrantLoc
ReentrantLock 是 Java 并发包(`java.util.concurrent.locks`)中提供的一种可重入互斥锁,它提供了比传统 `synchronized` 关键字更灵活、更强大的线程同步机制。与 `synchronized` 不同,`ReentrantLock` 允许显式地获取和释放锁,并支持公平锁策略、可中断锁等待以及条件变量等高级功能。
基本概念[编辑 | 编辑源代码]
什么是可重入锁?[编辑 | 编辑源代码]
可重入锁(Reentrant Lock)是指同一个线程可以多次获取同一把锁而不会导致死锁。例如,如果一个线程已经持有了锁,它可以再次请求该锁而不会被阻塞。`ReentrantLock` 实现了这一特性,因此被称为“可重入锁”。
ReentrantLock 的核心特性[编辑 | 编辑源代码]
- 可重入性:线程可以重复获取已持有的锁。
- 公平性:支持公平锁(先请求锁的线程先获得锁)和非公平锁(默认)。
- 可中断性:线程在等待锁的过程中可以响应中断。
- 超时机制:支持尝试获取锁的超时机制。
- 条件变量:可以通过 `Condition` 实现更精细的线程等待/通知机制。
基本用法[编辑 | 编辑源代码]
创建 ReentrantLock[编辑 | 编辑源代码]
`ReentrantLock` 的构造函数可以指定是否为公平锁:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock(); // 非公平锁(默认)
private final ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
}
获取和释放锁[编辑 | 编辑源代码]
使用 `lock()` 方法获取锁,`unlock()` 方法释放锁。通常需要在 `try-finally` 块中释放锁以确保异常时锁仍能被释放:
public void performTask() {
lock.lock(); // 获取锁
try {
// 临界区代码
System.out.println("Lock acquired by " + Thread.currentThread().getName());
} finally {
lock.unlock(); // 释放锁
System.out.println("Lock released by " + Thread.currentThread().getName());
}
}
可重入性示例[编辑 | 编辑源代码]
以下代码展示了 `ReentrantLock` 的可重入特性:
public void recursiveMethod(int depth) {
lock.lock();
try {
System.out.println("Depth: " + depth + ", Lock held by: " + Thread.currentThread().getName());
if (depth > 0) {
recursiveMethod(depth - 1); // 递归调用,仍能获取锁
}
} finally {
lock.unlock();
}
}
高级功能[编辑 | 编辑源代码]
公平锁与非公平锁[编辑 | 编辑源代码]
- 非公平锁(默认):不保证线程获取锁的顺序,可能导致某些线程“插队”。
- 公平锁:按照线程请求锁的顺序分配锁,避免饥饿现象,但可能降低吞吐量。
示例:
ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
尝试获取锁[编辑 | 编辑源代码]
`tryLock()` 方法尝试获取锁,如果锁不可用则立即返回 `false`,避免线程阻塞:
if (lock.tryLock()) {
try {
// 成功获取锁
} finally {
lock.unlock();
}
} else {
// 锁不可用
}
支持超时版本的 `tryLock`:
if (lock.tryLock(1, TimeUnit.SECONDS)) { // 等待最多 1 秒
try {
// 成功获取锁
} finally {
lock.unlock();
}
} else {
// 超时未获取锁
}
可中断锁[编辑 | 编辑源代码]
`lockInterruptibly()` 允许线程在等待锁的过程中响应中断:
try {
lock.lockInterruptibly(); // 可中断获取锁
try {
// 临界区代码
} finally {
lock.unlock();
}
} catch (InterruptedException e) {
// 处理中断
}
条件变量(Condition)[编辑 | 编辑源代码]
`Condition` 提供了类似 `Object.wait()` 和 `Object.notify()` 的功能,但更灵活:
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = 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();
}
}
实际应用案例[编辑 | 编辑源代码]
线程安全的计数器[编辑 | 编辑源代码]
以下是一个使用 `ReentrantLock` 实现的线程安全计数器:
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
生产者-消费者模型[编辑 | 编辑源代码]
使用 `ReentrantLock` 和 `Condition` 实现生产者-消费者模型:
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumer {
private final Queue<Integer> queue = new LinkedList<>();
private final int capacity = 5;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public void produce(int item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 等待队列非满
}
queue.add(item);
notEmpty.signal(); // 通知消费者
} finally {
lock.unlock();
}
}
public int consume() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 等待队列非空
}
int item = queue.remove();
notFull.signal(); // 通知生产者
return item;
} finally {
lock.unlock();
}
}
}
性能与最佳实践[编辑 | 编辑源代码]
- 在简单场景下,`synchronized` 的性能可能与 `ReentrantLock` 相当,但 `ReentrantLock` 在复杂场景下更灵活。
- 始终在 `finally` 块中释放锁,避免锁泄漏。
- 避免嵌套锁(多个锁的交叉持有),以减少死锁风险。
- 公平锁会降低吞吐量,仅在必要时使用。
与 synchronized 的对比[编辑 | 编辑源代码]
特性 | ReentrantLock | synchronized |
---|---|---|
可重入性 | 支持 | 支持 |
公平性 | 支持(可选) | 不支持 |
可中断性 | 支持 | 不支持 |
超时机制 | 支持 | 不支持 |
条件变量 | 支持(`Condition`) | 有限(`wait/notify`) |
锁释放 | 必须显式调用 `unlock()` | 自动释放 |
总结[编辑 | 编辑源代码]
`ReentrantLock` 是 Java 并发编程中的重要工具,提供了比 `synchronized` 更丰富的功能。它适用于需要高级锁特性的场景,如公平锁、可中断锁或条件变量。然而,对于简单的同步需求,`synchronized` 仍然是更简洁的选择。开发者应根据具体需求选择合适的同步机制。