Files
Knowledge/0 编程/java/ReentrantLock 常见用法.md
T

258 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
`ReentrantLock` 是 Java 并发包中提供的可重入锁,比 `synchronized` 更灵活。以下是其常见用法:
## 1. 基础锁使用
```java
import java.util.concurrent.locks.ReentrantLock;
public class BasicUsage {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
System.out.println("Count: " + count);
} finally {
lock.unlock(); // 必须放在 finally 中确保释放锁
}
}
}
```
## 2. 尝试获取锁(非阻塞)
```java
public class TryLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void tryLockMethod() {
if (lock.tryLock()) { // 尝试获取锁,立即返回结果
try {
// 获取锁成功,执行临界区代码
System.out.println("获取锁成功");
} finally {
lock.unlock();
}
} else {
// 获取锁失败,执行其他逻辑
System.out.println("获取锁失败,执行其他操作");
}
}
}
```
## 3. 带超时的尝试获取锁
```java
import java.util.concurrent.TimeUnit;
public class TimeoutLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void timeoutLockMethod() throws InterruptedException {
// 尝试在指定时间内获取锁
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
System.out.println("在1秒内获取到锁");
Thread.sleep(2000); // 模拟业务操作
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁超时");
}
}
}
```
## 4. 可中断的锁获取
```java
public class InterruptibleLockExample {
private final ReentrantLock lock = new ReentrantLock();
public void interruptibleMethod() throws InterruptedException {
lock.lockInterruptibly(); // 可响应中断的锁获取
try {
// 长时间操作
while (true) {
// 模拟长时间任务
if (Thread.currentThread().isInterrupted()) {
break;
}
}
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
```
## 5. 公平锁与非公平锁
```java
public class FairLockExample {
// 创建公平锁(按申请顺序获取锁)
private final ReentrantLock fairLock = new ReentrantLock(true);
// 创建非公平锁(默认,性能更高)
private final ReentrantLock unfairLock = new ReentrantLock();
}
```
## 6. 条件变量(Condition)
```java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean conditionMet = false;
public void awaitCondition() throws InterruptedException {
lock.lock();
try {
while (!conditionMet) {
condition.await(); // 释放锁并等待
}
// 条件满足,执行后续操作
System.out.println("条件满足,继续执行");
} finally {
lock.unlock();
}
}
public void signalCondition() {
lock.lock();
try {
conditionMet = true;
condition.signalAll(); // 唤醒所有等待线程
// 或使用 condition.signal() 唤醒一个线程
} finally {
lock.unlock();
}
}
}
```
## 7. 锁的查询方法
```java
public class LockStatusExample {
private final ReentrantLock lock = new ReentrantLock();
public void checkLockStatus() {
System.out.println("是否被当前线程持有: " + lock.isHeldByCurrentThread());
System.out.println("是否被锁定: " + lock.isLocked());
System.out.println("等待队列长度: " + lock.getQueueLength());
System.out.println("当前线程持有锁的次数: " + lock.getHoldCount());
// 获取锁信息
if (lock.isLocked()) {
System.out.println("持有锁的线程: " +
(lock.getOwner() != null ? lock.getOwner().getName() : "null"));
}
}
}
```
## 8. 综合示例:线程安全的队列
```java
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class BlockingQueue<T> {
private final Queue<T> queue = new LinkedList<>();
private final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
private final int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T item) throws InterruptedException {
lock.lock();
try {
while (queue.size() == capacity) {
notFull.await(); // 队列满,等待
}
queue.offer(item);
notEmpty.signal(); // 唤醒等待获取的线程
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while (queue.isEmpty()) {
notEmpty.await(); // 队列空,等待
}
T item = queue.poll();
notFull.signal(); // 唤醒等待放入的线程
return item;
} finally {
lock.unlock();
}
}
public int size() {
lock.lock();
try {
return queue.size();
} finally {
lock.unlock();
}
}
}
```
## 9. 与 synchronized 对比
```java
public class Comparison {
// synchronized 方式
public synchronized void syncMethod() {
// 临界区代码
}
// ReentrantLock 方式
private final ReentrantLock lock = new ReentrantLock();
public void lockMethod() {
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
}
}
```
## 最佳实践
1. **始终在 finally 块中释放锁**,防止异常导致锁无法释放
2. **不要嵌套过多 tryLock**,避免代码复杂度增加
3. **公平锁适用于等待时间差异大的场景**,但性能较低
4. **考虑使用 tryLock 避免死锁**
5. **合理使用 Condition 替代 Object 的 wait/notify**
## 选择建议
- 简单同步场景:优先使用 `synchronized`
- 需要高级功能(超时、可中断、公平锁):使用 `ReentrantLock`
- 需要多个等待条件:使用 `Condition`
- 性能敏感场景:测试后选择(通常 `synchronized` 性能更好)