ReentrantLock 源码深度解析
1. ReentrantLock 概述
ReentrantLock 是 Java 并发包中最重要的锁实现之一,它是一个可重入的互斥锁,具有与 synchronized 相同的基本行为和语义,但提供了更多的功能特性。
1.1 核心特性
- 可重入性:同一个线程可以多次获取同一个锁
- 公平性选择:支持公平锁和非公平锁两种模式
- 可中断性:支持可中断的锁获取操作
- 超时机制:支持带超时的锁获取尝试
- 条件变量:支持多个条件变量(Condition)
1.2 类结构
public class ReentrantLock implements Lock, java.io.Serializable {private static final long serialVersionUID = 7373984872572414699L;/** Synchronizer providing all implementation mechanics */private final Sync sync;// 抽象同步器基类abstract static class Sync extends AbstractQueuedSynchronizer {// 非公平锁获取逻辑final boolean nonfairTryAcquire(int acquires);// 锁释放逻辑protected final boolean tryRelease(int releases);// 判断当前线程是否持有锁protected final boolean isHeldExclusively();// 创建条件变量final ConditionObject newCondition();}// 非公平锁实现static final class NonfairSync extends Sync {protected final boolean tryAcquire(int acquires);}// 公平锁实现static final class FairSync extends Sync {protected final boolean tryAcquire(int acquires);}
}
2. 核心设计原理
2.1 组合优于继承
ReentrantLock 采用了组合模式,将同步逻辑委托给内部的 Sync 对象:
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
这种设计的优势:
- 灵活性:可以动态选择公平锁或非公平锁
- 可扩展性:易于添加新的锁策略
- 职责分离:ReentrantLock 负责对外接口,Sync 负责同步逻辑
2.2 状态表示
ReentrantLock 使用 AQS 的 state 字段表示锁的持有次数:
- state = 0:锁未被持有
- state > 0:锁被持有,数值表示重入次数
- state = 1:锁被持有一次
- state = 2:锁被重入一次
3. 核心方法详解
3.1 锁获取方法
3.1.1 lock() - 基本锁获取
public void lock() {sync.acquire(1);
}
执行流程:
- 调用
sync.acquire(1)
尝试获取锁 - 如果成功,直接返回
- 如果失败,线程进入等待队列,直到获取到锁
特点:
- 阻塞性:获取不到锁时会阻塞
- 不可中断:不支持中断响应
- 可重入:同一线程可以多次调用
3.1.2 lockInterruptibly() - 可中断锁获取
public void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);
}
执行流程:
- 检查当前线程是否被中断
- 如果已中断,抛出 InterruptedException
- 尝试获取锁,获取不到时进入等待队列
- 在等待过程中如果被中断,抛出 InterruptedException
特点:
- 响应中断:支持中断响应
- 异常处理:需要处理 InterruptedException
- 适用场景:需要响应中断的长时间等待
3.1.3 tryLock() - 尝试获取锁
public boolean tryLock() {return sync.nonfairTryAcquire(1);
}
执行流程:
- 直接调用
nonfairTryAcquire(1)
尝试获取锁 - 成功返回 true,失败返回 false
- 不会阻塞,立即返回结果
重要特点:
- 不遵循公平性:即使设置了公平锁,tryLock() 也不会遵循公平原则
- 立即返回:不会进入等待队列
- 非阻塞:适合快速失败场景
3.1.4 tryLock(long timeout, TimeUnit unit) - 超时锁获取
public boolean tryLock(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
执行流程:
- 将时间转换为纳秒
- 调用
tryAcquireNanos(1, nanos)
尝试获取锁 - 在指定时间内尝试获取,超时返回 false
- 支持中断响应
特点:
- 超时控制:避免无限等待
- 中断响应:支持中断操作
- 公平性遵循:会遵循公平锁的设置
3.2 锁释放方法
3.2.1 unlock() - 释放锁
public void unlock() {sync.release(1);
}
执行流程:
- 调用
sync.release(1)
释放锁 - 如果当前线程不是锁持有者,抛出 IllegalMonitorStateException
- 减少持有计数,计数为 0 时完全释放锁
重要特点:
- 计数递减:每次调用减少持有计数 1
- 完全释放:只有计数为 0 时才真正释放锁
- 异常检查:非持有者调用会抛出异常
3.3 条件变量方法
3.3.1 newCondition() - 创建条件变量
public Condition newCondition() {return sync.newCondition();
}
执行流程:
- 调用
sync.newCondition()
创建新的条件变量 - 返回 ConditionObject 实例
- 支持多个条件变量
使用示例:
ReentrantLock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();// 生产者-消费者模式
public void put(Object item) throws InterruptedException {lock.lock();try {while (queue.isFull()) {notFull.await(); // 等待不满条件}queue.put(item);notEmpty.signal(); // 通知不空条件} finally {lock.unlock();}
}
4. 公平锁与非公平锁
4.1 构造函数
// 默认非公平锁
public ReentrantLock() {sync = new NonfairSync();
}// 指定公平性
public ReentrantLock(boolean fair) {sync = fair ? new FairSync() : new NonfairSync();
}
4.2 非公平锁实现
4.2.1 NonfairSync.tryAcquire()
static final class NonfairSync extends Sync {protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
4.2.2 nonfairTryAcquire() 核心逻辑
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();// 情况1:锁未被持有if (c == 0) {if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// 情况2:当前线程已持有锁(重入)else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}
非公平锁特点:
- 抢占式:新来的线程可以直接尝试获取锁
- 性能优先:减少线程切换,提高吞吐量
- 饥饿可能:某些线程可能长时间获取不到锁
4.3 公平锁实现
4.3.1 FairSync.tryAcquire()
static final class FairSync extends Sync {protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();// 情况1:锁未被持有if (c == 0) {if (!hasQueuedPredecessors() && // 关键:检查是否有前驱节点compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}// 情况2:当前线程已持有锁(重入)else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}
公平锁特点:
- FIFO 顺序:严格按照等待顺序获取锁
- 公平性保证:避免饥饿现象
- 性能略低:需要检查等待队列,性能略低于非公平锁
4.3.2 hasQueuedPredecessors() 方法
public final boolean hasQueuedPredecessors() {Node t = tail;Node h = head;Node s;return h != t &&((s = h.next) == null || s.thread != Thread.currentThread());
}
判断逻辑:
- h == t:队列为空,没有等待者
- h != t 且 s == null:有等待者但后继节点为空(异常情况)
- h != t 且 s.thread != currentThread:有等待者且不是当前线程
5. 锁释放机制
5.1 tryRelease() 实现
protected final boolean tryRelease(int releases) {int c = getState() - releases;// 检查当前线程是否是锁持有者if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;// 如果持有计数为 0,完全释放锁if (c == 0) {free = true;setExclusiveOwnerThread(null);}setState(c);return free;
}
释放逻辑:
- 计数递减:state = state - releases
- 权限检查:只有持有者才能释放锁
- 完全释放:计数为 0 时清空持有线程
- 返回值:返回是否完全释放了锁
6. 状态查询方法
6.1 锁状态查询
// 查询锁是否被持有
public final boolean isLocked() {return sync.isLocked();
}// 查询当前线程是否持有锁
public boolean isHeldByCurrentThread() {return sync.isHeldExclusively();
}// 查询锁的持有次数
public int getHoldCount() {return sync.getHoldCount();
}// 查询锁的持有者
protected Thread getOwner() {return sync.getOwner();
}
6.2 队列状态查询
// 查询是否有线程等待获取锁
public final boolean hasQueuedThreads() {return sync.hasQueuedThreads();
}// 查询指定线程是否在等待队列中
public final boolean hasQueuedThread(Thread thread) {return sync.isQueued(thread);
}// 查询等待队列长度
public final int getQueueLength() {return sync.getQueueLength();
}// 获取等待线程集合
protected Collection<Thread> getQueuedThreads() {return sync.getQueuedThreads();
}
6.3 条件变量状态查询
// 查询指定条件上是否有等待线程
public boolean hasWaiters(Condition condition) {if (condition == null)throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}// 查询指定条件上等待线程的数量
public int getWaitQueueLength(Condition condition) {if (condition == null)throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}// 获取指定条件上等待的线程集合
protected Collection<Thread> getWaitingThreads(Condition condition) {if (condition == null)throw new NullPointerException();if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))throw new IllegalArgumentException("not owner");return sync.getWaitingThreads((AbstractQueuedSynchronizer.ConditionObject)condition);
}
7. 使用最佳实践
7.1 基本使用模式
class X {private final ReentrantLock lock = new ReentrantLock();public void m() {lock.lock(); // 获取锁try {// ... 方法体} finally {lock.unlock(); // 确保锁被释放}}
}
7.2 可中断锁获取
public void m() throws InterruptedException {lock.lockInterruptibly(); // 可中断的锁获取try {// ... 方法体} finally {lock.unlock();}
}
7.3 超时锁获取
public boolean m(long timeout, TimeUnit unit) throws InterruptedException {if (lock.tryLock(timeout, unit)) {try {// ... 方法体return true;} finally {lock.unlock();}}return false;
}
7.4 条件变量使用
class BoundedBuffer {private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition();private final Condition notEmpty = lock.newCondition();private final Object[] items = new Object[100];private int putptr, takeptr, count;public void put(Object x) throws InterruptedException {lock.lock();try {while (count == items.length)notFull.await();items[putptr] = x;if (++putptr == items.length) putptr = 0;++count;notEmpty.signal();} finally {lock.unlock();}}public Object take() throws InterruptedException {lock.lock();try {while (count == 0)notEmpty.await();Object x = items[takeptr];if (++takeptr == items.length) takeptr = 0;--count;notFull.signal();return x;} finally {lock.unlock();}}
}
8. 性能特点分析
8.1 公平锁 vs 非公平锁性能
特性 | 公平锁 | 非公平锁 |
---|---|---|
吞吐量 | 较低 | 较高 |
延迟 | 较高 | 较低 |
公平性 | 保证 | 不保证 |
饥饿 | 不会 | 可能 |
适用场景 | 对公平性要求高 | 对性能要求高 |
8.2 性能优化建议
- 默认使用非公平锁:除非对公平性有特殊要求
- 减少锁持有时间:尽快释放锁
- 避免嵌套锁:防止死锁
- 合理使用条件变量:避免虚假唤醒
9. 与 synchronized 的对比
9.1 功能对比
特性 | ReentrantLock | synchronized |
---|---|---|
可中断性 | ✅ 支持 | ❌ 不支持 |
超时机制 | ✅ 支持 | ❌ 不支持 |
公平性 | ✅ 可配置 | ❌ 不可配置 |
条件变量 | ✅ 多个 | ❌ 单一 |
性能 | 🟡 略高 | 🟢 稳定 |
使用复杂度 | 🟡 较高 | 🟢 简单 |
9.2 选择建议
-
使用 synchronized 的场景:
- 简单的同步需求
- 对性能要求不是特别高
- 希望代码简洁
-
使用 ReentrantLock 的场景:
- 需要可中断锁获取
- 需要超时机制
- 需要公平锁
- 需要多个条件变量
- 对性能有较高要求
10. 总结
10.1 核心优势
- 功能丰富:提供比 synchronized 更多的功能
- 性能优秀:非公平锁模式下性能优于 synchronized
- 灵活配置:支持公平锁和非公平锁
- 可扩展性:基于 AQS,易于扩展
10.2 设计亮点
- 组合模式:通过组合 Sync 对象实现不同策略
- 模板方法:利用 AQS 的模板方法模式
- 状态管理:通过 state 字段管理重入次数
- 异常处理:完善的异常检查和处理
10.3 应用场景
- 高并发系统:需要高性能锁的场景
- 复杂同步:需要多个条件变量的场景
- 响应式编程:需要可中断锁获取的场景
- 超时控制:需要避免无限等待的场景
ReentrantLock 是 Java 并发编程中最重要的锁实现之一,它提供了比 synchronized 更丰富的功能和更好的性能,是构建高性能并发系统的首选工具。