JAVA:ReentrantLock实现原理(附部分源码解析)
基本概念
在 Java 并发编程中,ReentrantLock 是与synchronized齐名的同步工具,但凭借显式控制、灵活策略、多条件等待等特性,成为复杂并发场景的优选。它并非凭空设计,而是基于 Java 并发包的核心基石 ——AQS(AbstractQueuedSynchronizer,抽象队列同步器) 构建,通过 “模板方法 + 策略模式” 实现了对锁的精细化管理。
本文先梳理 ReentrantLock 的核心特性,再深入源码逐一拆解实现逻辑
既然有了synchronized,还诞生了ReentrantLock,显然是有更加优秀的设计。
搞懂:ReentrantLock 到底有什么用?
ReentrantLock 的核心价值 —— 它解决了 synchronized 的哪些痛点?
-
可重入性
避免同一线程多次获取锁导致死锁(如递归调用场景) -
显式操作
支持手动控制锁的获取 / 释放时机,灵活度更高(synchronized 是隐式自动管理) -
公平 / 非公平双模式
可按需选择 “按顺序排队”(公平)或 “允许插队”(非公平,默认,性能更高) -
灵活的锁获取方式
支持非阻塞获取、超时获取、可中断获取,避免线程无限等待 -
多条件变量
支持多组线程独立等待 / 唤醒,解决 synchronized 单等待队列的局限性
接下来,从源码层面验证这些特性是如何落地的。
前置知识:ReentrantLock 的架构基石
ReentrantLock 的代码结构非常清晰,核心是 “外层 API 封装 + 内层同步器实现”,依赖 AQS 完成同步状态管理和线程排队。
整体架构图
ReentrantLock(对外暴露 API:lock()、unlock()、tryLock() 等)
↓ 组合关系(委托所有同步逻辑给 Sync)
Sync(抽象内部类,继承 AQS,实现可重入核心逻辑)
↓ 继承关系(策略模式:两种锁模式)
NonfairSync(非公平锁实现,默认)
FairSync(公平锁实现)
↓ 依赖 AQS 核心组件
AQS(抽象队列同步器):
- state:volatile 变量,记录锁的重入次数
- 同步队列:双向链表,存放获取锁失败的线程
- Condition 队列:每个 Condition 对应一个单向链表,存放等待唤醒的线程
关键铺垫:AQS 的核心作用
AQS 是 Java 并发同步的 “基础设施”,它帮 ReentrantLock 做了三件脏活累活:
-
状态管理:
用volatile int state记录锁的状态(0 = 未占用,>0 = 重入次数); -
线程排队:
用双向链表实现 “同步队列”,让获取锁失败的线程排队等待; -
线程阻塞 / 唤醒:
用LockSupport.park()/unpark()控制线程的阻塞与唤醒。
ReentrantLock 只需通过重写 AQS 的 钩子方法(如 tryAcquire、tryRelease)定义 “如何获取 / 释放锁”,剩下的排队、阻塞逻辑全由 AQS 搞定 —— 这就是 “模板方法模式” 的精髓。
逐个拆解:特性与源码的对应关系
特性 1:可重入性 —— 同一线程能多次抢锁,不会死锁
什么是可重入性?
比如线程 A 已经获取了锁,在没释放的情况下再次调用 lock(),不会被自己阻塞(死锁),而是会增加 “重入次数”;只有当重入次数减到 0 时,锁才真正释放给其他线程。
典型场景:递归调用中加锁
ReentrantLock lock = new ReentrantLock();
public void recursiveMethod() {
lock.lock(); // 第 1 次获取,state=1
try {
if (条件) {
recursiveMethod(); // 递归调用,第 2 次获取,state=2
}
} finally {
lock.unlock(); // 每次释放,state 减 1,直到 0 才释放锁
}
}
源码如何实现?
核心依赖 AQS 的state变量和 tryAcquire/tryRelease 方法。
第一步:获取锁时的重入判断(tryAcquire)ReentrantLock 的 lock() 最终会调用Sync 的tryAcquire方法,我们以默认的非公平锁为例:
// NonfairSync 类的 tryAcquire 方法(核心重入逻辑)
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread(); // 当前线程
int c = getState(); // 获取 AQS 的 state 变量(volatile 保证可见性)
// 情况 1:锁未被占用(state=0)
if (c == 0) {
// 用 CAS 尝试抢占锁(CAS 是原子操作,避免并发抢锁冲突)
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); // 标记当前线程为锁的持有者
return true; // 抢占成功
}
}
// 情况 2:当前线程已经是锁的持有者(重入关键逻辑)
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; // 重入次数 +1(acquires 通常是 1)
if (nextc < 0) { // 防止 int 溢出(虽然实际中很难触发)
throw new Error("Maximum lock count exceeded");
}
setState(nextc); // 更新 state(无需 CAS,因为只有持有者能执行这里)
return true; // 重入成功
}
// 情况 3:锁被其他线程占用,返回 false
return false;
}
关键细节:
voltile保证可见性:state 是 volatile 修饰,确保一个线程修改后,其他线程能立即看到最新值;
重入时无需 CAS:
因为只有当前持有者线程能进入 else if 分支,不存在并发修改 state 的问题。
释放锁时的重入次数递减(tryRelease)
释放锁的逻辑在 Sync 的 tryRelease 方法中,无论公平 / 非公平锁,释放逻辑都一样:
// Sync 类的 tryRelease 方法
protected final boolean tryRelease(int releases) {
int c = getState() - releases; // 重入次数 -1(releases 通常是 1)
// 关键校验:只有锁的持有者才能释放锁
if (Thread.currentThread() != getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException(); // 非持有者释放,抛异常
}
boolean free = false;
if (c == 0) { // 重入次数减到 0,说明锁真正释放了
free = true;
setExclusiveOwnerThread(null); // 清空锁持有者标记
}
setState(c); // 更新 state(即使没释放,也要更新重入次数)
return free; // 返回是否真正释放了锁(供 AQS 唤醒后续线程)
}
为什么 return free 而不是直接返回 true?
因为 AQS 只有在锁真正释放(free=true)时,才会去唤醒同步队列中等待的线程;如果只是重入次数减少(没到 0),锁还被占用,不需要唤醒其他线程。
小结:可重入性的本质
用 state 记录重入次数,同一线程再次抢锁时 state 递增,释放时递减,直到 state=0 才真正释放锁 —— 本质是 “线程身份校验 + 计数器” 的结合。
特性 2:显式锁操作 —— 手动控制锁的生命周期
什么是显式操作?synchronized 是 “隐式锁”:进入代码块自动获取锁,离开(正常执行完 / 抛异常)自动释放锁;ReentrantLock 是 “显式锁”: 必须手动调用 lock() 获取、unlock() 释放,且 unlock() 必须放在 finally 中(防止异常导致锁泄漏)。
源码如何实现?
以非公平锁为例,拆解 lock() 和 unlock() 的完整流程。
第一步:lock() 方法的完整执行流程ReentrantLock 的 lock() 只是一个入口,真正的逻辑在 NonfairSync 中:
// ReentrantLock 的 lock 方法
public void lock() {
sync.lock(); // 委托给 Sync 实现(非公平锁就是 NonfairSync)
}
// NonfairSync 的 lock 方法(非公平锁的核心抢锁逻辑)
final void lock() {
// 第一步:尝试“插队”抢锁(非公平特性的体现)
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
} else {
// 第二步:抢锁失败,进入 AQS 的排队流程
acquire(1);
}
}
acquire(1) 是 AQS 的模板方法,定义了 “抢锁失败后如何排队” 的通用逻辑,流程如下:
// AQS 的 acquire 方法(模板方法,固定流程)
public final void acquire(int arg) {
// 1. 再次尝试抢锁(tryAcquire 是 ReentrantLock 重写的钩子方法)
// 2. 若失败,把当前线程包装成节点加入同步队列(addWaiter)
// 3. 让节点在队列中阻塞等待(acquireQueued)
if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
selfInterrupt(); // 若等待中被中断,恢复中断状态
}
}
拆解这三个步骤:
再次尝试抢锁(tryAcquire):
和之前讲的重入逻辑一致,可能有其他线程刚释放锁,再试一次能减少排队开销;
加入同步队列(addWaiter):
把当前线程包装成 Node 节点(包含线程引用、等待状态等),加入 AQS 的双向链表尾部;
阻塞等待(acquireQueued):
让节点在队列中 “休眠”,直到前驱节点释放锁并唤醒自己,然后再次尝试抢锁。
第二步:unlock() 方法的完整执行流程
释放锁的入口同样简单,核心逻辑在 AQS 的 release 方法:
// ReentrantLock 的 unlock 方法
public void unlock() {
sync.release(1); // 委托给 Sync 实现
}
// AQS 的 release 方法(模板方法)
public final boolean release(int arg) {
// 1. 尝试释放锁(tryRelease 是 ReentrantLock 重写的钩子方法)
if (tryRelease(arg)) {
Node h = head; // 同步队列的头节点
if (h != null && h.waitStatus != 0) {
unparkSuccessor(h); // 2. 唤醒头节点的后继节点(让下一个线程抢锁)
}
return true;
}
return false;
}
unparkSuccessor(h) 会通过 LockSupport.unpark(后继线程) 唤醒等待的线程,被唤醒的线程会在 acquireQueued 中再次尝试抢锁。
为什么必须用 finally 释放锁?
如果 lock() 后,临界区代码抛异常,unlock() 就不会执行,导致锁一直被占用,其他线程永远抢不到 —— 这就是 “锁泄漏”。
示例:
// 错误写法:unlock() 不在 finally 中
lock.lock();
doSomething(); // 若这里抛异常,unlock() 不执行,锁泄漏
lock.unlock();
// 正确写法
lock.lock();
try {
doSomething();
} finally {
lock.unlock(); // 无论是否抛异常,都能释放锁
}
小结:显式操作的本质
通过 lock() 触发抢锁 / 排队流程,unlock() 触发释放 / 唤醒流程,开发者需手动控制生命周期 —— 灵活但需谨慎(必须配 finally)。
特性 3:公平 / 非公平双模式 —— 按需选择抢锁策略
两种模式的核心区别
非公平锁(默认):
线程抢锁时会先 “插队”(直接 CAS 抢锁),插队失败再排队;刚释放锁的线程可能立即重新抢锁,性能高,但可能导致某些线程长期抢不到锁(饥饿)。
公平锁:
线程必须按 “请求顺序” 排队,不能插队;先请求的线程先获取锁,避免饥饿,但性能低(线程切换频繁)。
源码如何实现?
两种模式的核心差异只在 tryAcquire 方法的一个判断 ——是否检查同步队列。
公平锁的 tryAcquire 方法(FairSync 类)
static final class FairSync extends Sync {
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 关键区别:比非公平锁多了 hasQueuedPredecessors() 判断
if (!hasQueuedPredecessors() && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 重入逻辑和非公平锁完全一致
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
hasQueuedPredecessors() 是公平锁的 “灵魂方法”,作用是判断同步队列中是否有比当前线程更早请求锁的线程。如果有,当前线程不能插队,必须排队。
我们看它的实现:
// AQS 的 hasQueuedPredecessors 方法
public final boolean hasQueuedPredecessors() {
Node t = tail; // 队列尾节点
Node h = head; // 队列头节点
Node s;
// 逻辑:队列不为空(h != t),且头节点的后继节点(s)存在,且 s 的线程不是当前线程
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
举个例子:
同步队列中有线程 A(头节点后继)、线程 B 排队;
此时锁释放,线程 C 来抢锁:hasQueuedPredecessors() 返回 true,线程 C 不能插队,必须排到队尾;只有线程 A 能抢锁成功,实现 “先到先得”。
非公平锁的 “插队” 逻辑
非公平锁的 tryAcquire 没有 hasQueuedPredecessors() 判断,只要 state=0,就直接 CAS 抢锁:
// 非公平锁的 tryAcquire 核心逻辑
if (c == 0) {
// 直接抢,不管队列有没有等待的线程
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
比如:
线程 A 释放锁,state=0;
此时线程 B 正在队列中等待,线程 C(C是新来的线程,并不是在队列中排队的线程) 突然来抢锁:非公平锁下,C 可能先抢到锁,B 继续等待。
两种模式的性能对比非公平锁性能更高,
减少线程切换:
刚释放锁的线程可能还在 CPU 缓存中,立即重新抢锁能避免线程从 “休眠” 到 “唤醒” 的切换开销;
减少排队开销:
插队成功的线程无需进入队列,节省链表操作成本。公平锁只在 “必须保证请求顺序” 的场景下使用(如金融交易的顺序性),大部分场景优先选非公平锁。
小结:双模式的本质
通过 hasQueuedPredecessors() 方法控制是否 “插队”,实现公平与非公平的策略切换 —— 本质是 “性能” 与 “公平性” 的权衡。
特性 4:灵活的锁获取方式 —— 拒绝无限等待
synchronized 只有 “阻塞抢锁” 一种方式:抢不到就一直等,无法中断、无法超时。ReentrantLock 提供了三种灵活的获取方式,解决了这个问题。
- 方式 1:非阻塞获取(tryLock())
调用后立即返回,成功返回true,失败返回false,不会阻塞线程。适合 “能抢就抢,抢不到就做别的” 场景(如降级返回缓存数据)。
// ReentrantLock 的 tryLock 方法
public boolean tryLock() {
// 调用非公平的尝试抢锁逻辑(即使是公平锁,tryLock() 也默认非公平)
return sync.nonfairTryAcquire(1);
}
// Sync 类的 nonfairTryAcquire 方法(和非公平锁的 tryAcquire 逻辑一致)
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false; // 抢不到立即返回 false
}
使用场景:
if (lock.tryLock()) { // 抢锁成功
try {
doCriticalWork();
} finally {
lock.unlock();
}
} else {
return getCacheData(); // 抢不到锁,返回缓存,降级处理
}
- 方式 2:超时获取(tryLock(long timeout, TimeUnit unit))
在指定时间内尝试抢锁,超时前抢到返回true,超时仍未抢到返回false。适合 “不能无限等,但可以等一会儿” 的场景(如用户请求超时控制)。
// ReentrantLock 的 tryLock 超时方法
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
// 转成纳秒,调用 AQS 的 tryAcquireNanos 方法
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
// AQS 的 tryAcquireNanos 方法(核心是超时逻辑)
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {
if (Thread.interrupted()) // 先检查是否被中断
throw new InterruptedException();
// 1. 先尝试抢锁
// 2. 抢不到,计算剩余超时时间,进入阻塞等待
return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos 会让线程阻塞一段时间,每隔一段时间检查剩余时间,超时则返回 false,同时支持中断。
- 方式 3:可中断获取(lockInterruptibly())
抢锁时如果被其他线程调用interrupt(),会立即抛出InterruptedException,停止等待。适合 “需要响应中断信号” 的场景(如优雅关闭线程池时中断等待的线程)。
// ReentrantLock 的 lockInterruptibly 方法
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1); // 调用 AQS 的方法
}
// AQS 的 acquireInterruptibly 方法
public final void acquireInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg)) {
// 抢锁失败,进入可中断的等待流程
doAcquireInterruptibly(arg);
}
}
doAcquireInterruptibly 与普通的 acquireQueued 区别在于:阻塞时如果收到中断信号,会直接抛出异常,不再继续等待。
小结:灵活获取的本质
通过 “非阻塞 CAS 尝试”、“超时计时”、“中断响应” 三种机制,打破了 synchronized 的 “无限阻塞” 限制 —— 本质是 “给线程一个‘退路’”。
特性 5:多条件变量(Condition)—— 分组等待 / 唤醒
什么是条件变量?Condition 是 ReentrantLock 提供的 “线程分组等待工具”,类似 Object 的 wait()/notify(),但支持多组线程独立等待。
举个例子:
生产者 - 消费者模型中,有两类线程:
- 消费者线程:队列空时等待;
- 生产者线程:队列满时等待。
如果用 synchronized 的 wait()/notify():
所有等待线程都在同一个队列中,notify() 可能唤醒错误类型的线程(比如队列空时唤醒了生产者),导致 “唤醒浪费”。
如果用 ReentrantLock 的 Condition:
可以创建两个 Condition:notEmpty(供消费者等待)和 notFull(供生产者等待);
生产者添加元素后,只唤醒 notEmpty 上的消费者;消费者移除元素后,只唤醒notFull上的生产者 —— 精准唤醒,效率更高。
Condition 的核心是 “每个 Condition 对应一个独立的等待队列”,底层由 AQS 的内部类ConditionObject 实现。
第一步:创建 Condition
ReentrantLock lock = new ReentrantLock();
// 通过 lock 创建 Condition,本质是 new 了 AQS 的 ConditionObject
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
第二步:Condition 的核心方法
Condition 有三个核心方法:await()(等待)、signal()(唤醒一个)、signalAll()(唤醒所有)。
1. await() 方法:线程释放锁并进入 Condition 队列等待
// ConditionObject 的 await 方法(简化核心逻辑)
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 1. 把当前线程包装成 Node,加入 Condition 的等待队列(单向链表)
Node node = addConditionWaiter();
// 2. 释放当前持有的锁(必须释放,否则其他线程无法操作共享资源)
int savedState = fullyRelease(node);
int interruptMode = 0;
// 3. 阻塞等待,直到被 signal() 唤醒或被中断
while (!isOnSyncQueue(node)) { // 判断是否被移到 AQS 同步队列
LockSupport.park(this); // 线程休眠
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break; // 被中断,退出循环
}
// 4. 被唤醒后,重新竞争 AQS 锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 5. 清理 Condition 队列中的无效节点
if (node.nextWaiter != null)
unlinkCancelledWaiters();
// 6. 处理中断
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
关键流程:
释放锁:fullyRelease(node) 会调用 tryRelease 释放锁,并重设 state 为 0;
Condition 队列:
和 AQS 的同步队列是两个独立的队列,await() 把线程放到 Condition 队列,signal() 把线程移到同步队列;
重新抢锁:
被唤醒后,线程不会直接执行,而是要重新进入AQS同步队列抢锁 —— 保证同一时刻只有一个线程执行临界区。
2. signal() 方法:唤醒 Condition 队列中的一个线程
// ConditionObject 的 signal 方法
public final void signal() {
// 校验:只有锁的持有者才能唤醒线程
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter; // Condition 队列的头节点
if (first != null)
doSignal(first); // 唤醒头节点
}
// doSignal 核心逻辑:把 Condition 队列的节点移到 AQS 同步队列
private void doSignal(Node first) {
do {
if ((firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
// transferForSignal:把节点从 Condition 队列移到 AQS 同步队列
final boolean transferForSignal(Node node) {
// 修改节点状态为“等待唤醒”
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 把节点加入 AQS 同步队列的尾部
Node p = enq(node);
int ws = p.waitStatus;
// 如果前驱节点状态无效,唤醒当前节点(让它去抢锁)
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
关键逻辑:signal() 不会直接执行线程,只是把线程从 Condition 队列 “移到” AQS 同步队列;线程要等抢到 AQS 锁后,才能继续执行 await() 之后的代码。
小结:多条件变量的本质
通过 “多个独立的 Condition 队列” 实现线程分组,配合 AQS 同步队列完成 “等待 - 唤醒 - 抢锁” 流程 —— 本质是 “精准控制线程的等待与唤醒”。
架构回顾:ReentrantLock 为什么这么设计?
ReentrantLock 的架构是 “策略模式 + 模板方法模式” 的经典应用:
策略模式:NonfairSync 和 FairSync 是两种策略,ReentrantLock 通过构造函数(new ReentrantLock(true) 选公平锁)切换策略,灵活且易于扩展;
模板方法模式:AQS 定义 “抢锁 - 排队 - 释放 - 唤醒” 的模板流程,ReentrantLock 只需要重写 tryAcquire/tryRelease 等钩子方法,无需关心底层队列和线程控制 —— 极大简化了锁的实现。
核心依赖 AQS 的原因:AQS 封装了并发编程的共性问题(线程排队、阻塞 / 唤醒、状态管理),ReentrantLock 只需聚焦 “锁的特有逻辑”(可重入、公平 / 非公平),符合 “单一职责” 原则。
ReentrantLock vs synchronized:到底该用哪个?
| 维度 | ReentrantLock | synchronized |
|---|---|---|
| 锁类型 | 显式锁(手动控制获取与释放) | 隐式锁(自动获取与释放) |
| 可重入性 | 支持(同一线程可多次获取锁) | 支持(同一线程可多次获取锁) |
| 公平性 | 可选择(构造函数参数指定公平/非公平) | 仅非公平(无法保证获取锁的顺序) |
| 锁获取方式 | 支持阻塞、非阻塞(tryLock())、超时(tryLock(timeout))、可中断(lockInterruptibly()) | 仅支持阻塞获取(抢不到锁则一直等待) |
| 条件变量 | 支持多个(通过newCondition()创建,实现分组等待/唤醒) | 仅支持一个(基于对象本身的wait()/notify()) |
| 性能(JDK 1.6+) | 高并发场景下性能略优 | 经过优化后性能接近,简单场景更高效 |
| 易用性 | 较复杂(需手动释放锁,通常配合finally使用) | 简单(自动释放锁,不易出现锁泄漏) |
| 功能扩展 | 支持锁中断、超时控制、多条件队列等扩展功能 | 功能固定,无扩展接口 |
| 适用场景 | 复杂并发场景(如需要超时控制、中断响应、多条件等待等) | 简单同步场景(如单例模式、简单代码块同步) |
最佳实践:
- 简单场景(如单例、简单同步块):用 synchronized,简单不易出错;
- 复杂场景:
- 需要超时 / 中断:用 ReentrantLock 的 tryLock(timeout) 或 lockInterruptibly();
- 需要多条件等待:用 ReentrantLock + Condition;
- 需要公平锁:用 ReentrantLock(true);
- 高并发场景:两者均可,ReentrantLock 性能略优,但差距不大。
六、总结ReentrantLock是Java并发编程的 “瑞士军刀”,其核心设计围绕 “灵活” 和 “可控”:
- 用
state实现可重入,用hasQueuedPredecessors()实现公平性; - 用显式的
lock()/unlock()控制生命周期; - 用
tryLock()/lockInterruptibly()提供灵活获取方式; - 用 Condition 实现多组线程的精准唤醒,底层依赖 AQS 完成排队和线程控制。
看懂 ReentrantLock 的源码,不仅能更好地使用它,更能理解并发编程的核心思想 ——如何在多线程竞争下保证数据一致性,同时兼顾性能与灵活性。
Studying will never be ending.
▲如有纰漏,烦请指正~~
更多推荐
所有评论(0)