ReentrantLock通过维护线程持有标识和重入计数实现可重入性,同一线程可多次获取锁,计数器递增,释放时递减至零才真正释放;示例中increment调用nestedIncrement体现重入特性,配合try-finally确保释放锁;支持公平与非公平模式,默认非公平性能更高;提供lock、unlock、tryLock等方法,增强并发控制灵活性,需注意成对调用及异常安全。

在Java中,ReentrantLock 是一种可重入的互斥锁,它提供了比 synchronized 更灵活的锁定机制。所谓“可重入”,指的是同一个线程可以多次获取同一把锁而不会造成死锁。下面详细解析 ReentrantLock 如何实现可重入特性,并展示其基本使用方式。
什么是可重入锁
可重入锁允许当前持有锁的线程再次进入被该锁保护的代码块。比如一个线程已经获得了某个锁,当它再次请求这个锁时,可以直接获得,而不是被阻塞。JVM 中的 synchronized 关键字是可重入的,而 ReentrantLock 也实现了同样的语义。
ReentrantLock 的可重入性是通过内部维护一个持有锁的线程标识和一个计数器来实现的:当线程第一次获取锁时,计数器设为1;每次重入时计数器加1;释放锁时计数器减1,直到为0才真正释放锁。
ReentrantLock 基本使用示例
以下是一个典型的 ReentrantLock 使用场景:
立即学习“Java免费学习笔记(深入)”;
import java.util.concurrent.locks.ReentrantLock;
public class Counter {
private int count = 0;
private final ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock(); // 获取锁
try {
count++;
System.out.println(Thread.currentThread().getName() + " count: " + count);
if (count < 5) {
nestedIncrement(); // 调用另一个需要同一把锁的方法
}
} finally {
lock.unlock(); // 释放锁
}
}
public void nestedIncrement() {
lock.lock(); // 同一线程可重复进入
try {
count++;
System.out.println(Thread.currentThread().getName() + " (nested) count: " + count);
} finally {
lock.unlock();
}
}
}
在这个例子中,increment() 方法调用了 nestedIncrement(),两个方法都使用了同一个 ReentrantLock 实例加锁。由于 ReentrantLock 支持可重入,同一线程不会因为重复加锁而阻塞自己。
公平锁与非公平锁
ReentrantLock 构造时可以选择是否启用公平策略:
- new ReentrantLock():默认是非公平锁,性能更高,但可能造成线程“饥饿”
- new ReentrantLock(true):公平锁,按等待时间顺序获取锁,更公平但开销略大
公平锁会记录等待队列,确保等待最久的线程优先获取锁;而非公平锁允许插队,可能导致新来的线程先于等待中的线程获取锁。
常用方法说明
ReentrantLock 提供了一些比 synchronized 更丰富的控制能力:
- lock():获取锁,阻塞直到成功
- unlock():释放锁,必须由持有锁的线程调用
- tryLock():尝试获取锁,立即返回 boolean,不会阻塞
- tryLock(long timeout, TimeUnit unit):在指定时间内尝试获取锁
- isHeldByCurrentThread():判断当前线程是否持有此锁
- getHoldCount():返回当前线程持有此锁的次数(重入次数)
例如,使用 tryLock 避免无限等待:
if (lock.tryLock(3, TimeUnit.SECONDS)) {
try {
// 执行操作
} finally {
lock.unlock();
}
} else {
System.out.println("未能获取锁");
}
注意事项
使用 ReentrantLock 时必须注意以下几点:
- 务必在 finally 块中释放锁,防止因异常导致锁无法释放
- 不能跨线程重入,只能是同一线程多次获取
- lock() 和 unlock() 必须成对出现,避免死锁或 IllegalMonitorStateException
- 建议优先考虑 synchronized,除非需要 tryLock、超时、中断等高级功能
基本上就这些。ReentrantLock 在需要更细粒度控制并发时非常有用,理解其可重入机制有助于写出更安全的多线程代码。









