ReentrantLock 原理与实战
ReentrantLock 是基于 AQS 实现的可重入锁,提供更灵活的锁操作,支持公平/非公平、中断响应和条件变量。
一、核心特性
1.1 与 synchronized 对比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现层级 | JVM 内置(字节码) | JDK API(Lock 接口) |
| 公平性 | 非公平(默认) | 支持公平/非公平 |
| 中断响应 | ❌ 不支持 | ✅ 支持 lockInterruptibly() |
| 超时获取 | ❌ 不支持 | ✅ 支持 tryLock(timeout) |
| 条件变量 | wait/notify(单一) | Condition(多个) |
| 性能 | JDK 1.8+ 优化后高 | 高并发场景更优 |
| 使用便捷 | 简单(自动加解锁) | 需手动 try/finally |
1.2 基本使用
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区代码
} finally {
lock.unlock();
}
二、实现原理
2.1 继承 AQS
public class ReentrantLock implements Lock, java.io.Serializable {
// 内部同步器
private final Sync sync;
// 抽象同步器(继承 AQS)
abstract static class Sync extends AbstractQueuedSynchronizer {
// state 表示锁的重入次数
}
// 非公平锁
static final class NonfairSync extends Sync {
protected final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
}
// 公平锁
static final class FairSync extends Sync {
protected final void lock() {
acquire(1);
}
}
}
2.2 state 状态
// state 表示锁的重入次数
// state = 0:锁空闲
// state > 0:锁被占用,值为重入次数
// 获取锁
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
}
// 重入
if (current == getExclusiveOwnerThread()) {
setState(getState() + 1);
return true;
}
// 释放锁
int c = getState() - 1;
setState(c);
if (c == 0) {
setExclusiveOwnerThread(null);
}
2.3 非公平锁实现
final void lock() {
// 1. 直接 CAS 尝试获取(插队)
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 2. 失败则加入队列
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// CAS 获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 重入
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
2.4 公平锁实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 公平性检查:队列中有等待线程则不获取
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (current == getExclusiveOwnerThread()) {
// 重入
int nextc = c + acquires;
setState(nextc);
return true;
}
return false;
}
关键差异:
// 公平锁增加检查
if (!hasQueuedPredecessors()) {
// 队列中无等待线程才尝试获取
}
三、条件变量(Condition)
3.1 与 wait/notify 对比
// synchronized + wait/notify
synchronized (lock) {
while (condition) {
lock.wait();
}
// 业务逻辑
lock.notify();
}
// ReentrantLock + Condition
lock.lock();
try {
while (condition) {
condition.await();
}
// 业务逻辑
condition.signal();
} finally {
lock.unlock();
}
3.2 多条件支持
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
// 生产者
lock.lock();
try {
while (count == items.length)
notFull.await();
// 生产
notEmpty.signal();
} finally {
lock.unlock();
}
// 消费者
lock.lock();
try {
while (count == 0)
notEmpty.await();
// 消费
notFull.signal();
} finally {
lock.unlock();
}
3.3 Condition 实现
// await() - 释放锁并加入条件队列
public final void await() throws InterruptedException {
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
// 被唤醒后检查中断
}
acquireQueued(node, savedState);
}
// signal() - 转移到同步队列
public final void signal() {
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
四、中断响应
4.1 lockInterruptibly()
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// AQS 实现
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
4.2 使用场景
// 可中断的锁获取
try {
lock.lockInterruptibly();
// 业务逻辑
} catch (InterruptedException e) {
// 处理中断
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread())
lock.unlock();
}
4.3 与 synchronized 对比
// synchronized 无法响应中断
synchronized (lock) {
// 阻塞等待,无法中断
}
// ReentrantLock 可中断
lock.lockInterruptibly();
五、超时获取
5.1 tryLock(timeout)
// 尝试获取锁,超时返回
if (lock.tryLock(1, TimeUnit.SECONDS)) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
} else {
// 超时处理
System.out.println("获取锁超时");
}
5.2 非阻塞尝试
// 立即尝试获取
if (lock.tryLock()) {
try {
// 业务逻辑
} finally {
lock.unlock();
}
} else {
// 获取失败,执行其他逻辑
}
六、最佳实践
6.1 必须 finally 解锁
// ❌ 错误示例
lock.lock();
// 业务逻辑(异常时不解锁)
// ✅ 正确示例
lock.lock();
try {
// 业务逻辑
} finally {
lock.unlock();
}
6.2 选择公平锁
// 默认非公平锁(吞吐量大)
ReentrantLock lock = new ReentrantLock();
// 公平锁(避免饥饿)
ReentrantLock fairLock = new ReentrantLock(true);
6.3 避免死锁
// 固定顺序获取锁
public void transfer(Account from, Account to) {
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if (fromHash < toHash) {
from.lock.lock();
to.lock.lock();
} else {
to.lock.lock();
from.lock.lock();
}
try {
// 转账逻辑
} finally {
from.lock.unlock();
to.lock.unlock();
}
}
6.4 读写锁(ReentrantReadWriteLock)
ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
ReadWriteLock readLock = rwLock.readLock();
ReadWriteLock writeLock = rwLock.writeLock();
// 读操作(共享)
readLock.lock();
try {
return data;
} finally {
readLock.unlock();
}
// 写操作(独占)
writeLock.lock();
try {
data = newValue;
} finally {
writeLock.unlock();
}
七、总结
ReentrantLock 核心要点:
| 特性 | 说明 | 优势 |
|---|---|---|
| 可重入 | 同一线程可多次获取 | 避免死锁 |
| 公平性 | 支持公平/非公平 | 灵活选择 |
| 中断响应 | lockInterruptibly() | 可中断等待 |
| 超时获取 | tryLock(timeout) | 避免无限等待 |
| 条件变量 | 多 Condition | 精确控制 |
synchronized 优先,特殊场景(公平、中断、多条件)用 ReentrantLock。