基本概念

在 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() 最终会调用SynctryAcquire方法,我们以默认的非公平锁为例:


// 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保证可见性:
statevolatile 修饰,确保一个线程修改后,其他线程能立即看到最新值

重入时无需 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(),但支持多组线程独立等待。

举个例子:
生产者 - 消费者模型中,有两类线程:

  • 消费者线程:队列空时等待;
  • 生产者线程:队列满时等待。

如果用 synchronizedwait()/notify()
所有等待线程都在同一个队列中notify() 可能唤醒错误类型的线程(比如队列空时唤醒了生产者),导致 “唤醒浪费”。

如果用 ReentrantLock Condition
可以创建两个 ConditionnotEmpty(供消费者等待)和 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 的架构是 “策略模式 + 模板方法模式” 的经典应用:

策略模式:
NonfairSyncFairSync 是两种策略,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 性能略优,但差距不大。
      六、总结
      ReentrantLockJava 并发编程的 “瑞士军刀”,其核心设计围绕 “灵活” 和 “可控”:
  • state 实现可重入,用 hasQueuedPredecessors() 实现公平性;
  • 用显式的 lock()/unlock() 控制生命周期;
  • tryLock()/lockInterruptibly()提供灵活获取方式;
  • 用 Condition 实现多组线程的精准唤醒,底层依赖 AQS 完成排队和线程控制。

看懂 ReentrantLock 的源码,不仅能更好地使用它,更能理解并发编程的核心思想 ——如何在多线程竞争下保证数据一致性,同时兼顾性能与灵活性。


Studying will never be ending.

▲如有纰漏,烦请指正~~

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐