下面我把 JDK 8 中 new ReentrantLock()(非公平)对象上调用 lock.lock()lock.unlock(),在运行链路上会依次触发的相关方法源码按执行顺序列出来(用简化但几乎与 OpenJDK 8 等价的代码段),并在每段代码后紧跟逐步讲解。目标是让你能按“调用栈顺序”看到 AQS + ReentrantLock 的整个工作流,并理解每一步为什么要这样做。

说明:

  • 我只列出「在典型路径上」会被执行的核心方法(即非公平锁、第一次获取时 CAS 失败导致入队的路径,以及释放时唤醒后继的路径)。
  • 为了阅读性,我把部分辅助函数(如 CAS 原语)以注释形式说明,但保留源码最关键逻辑。
  • 所有代码片段基于 OpenJDK8 实现风格(已简化注释和异常处理以便理解),并在每段后用通俗语言解释。

A. lock() 的总体入口与主要分支

1) java.util.concurrent.locks.ReentrantLock#lock()

public void lock() {
    sync.lock();
}

说明ReentrantLock 对外的 lock() 只是一个简单代理,把请求交给内部 SyncNonfairSyncFairSync)来处理。对于 new ReentrantLock() 默认是 NonfairSync(非公平)。


2) ReentrantLock.NonfairSync#lock()(非公平实现)

static final class NonfairSync extends Sync {
    final void lock() {
        // 快速路径:试图用 CAS 直接设置 state=1,成为持有者
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            // 慢路径:尝试通过 AQS.acquire(1) 去入队/等待
            acquire(1);
    }
}

解释

  • 非公平锁尝试无队列直接抢占compareAndSetState(0,1))。如果成功,就拿到锁,不需要排队。
  • 如果失败(例如其它线程已持有锁或多线程并发),走 AQS 的 acquire(1) 模板方法(进入排队/阻塞流程)。

B. acquire(1) 的调用链(入队与等待)

3) AbstractQueuedSynchronizer#acquire(int arg)

public final void acquire(int arg) {
    // 先尝试子类的 tryAcquire(arg)(子类 ReentrantLock.Sync 实现)
    if (!tryAcquire(arg) &&
        // 如果尝试失败,就把当前线程入队并在队列中等待获取(acquireQueued)
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 若等待期间被中断,置中断状态
        selfInterrupt();
}

解释

  • acquire 是 AQS 的模板:先快速尝试(子类实现),失败则把线程包装成 Node 入队(addWaiter),再在队列中循环等待(acquireQueued)。
  • arg 在 ReentrantLock 中就是 1(每次占用一个“许可”——即一次锁定)。

4) ReentrantLock.Sync#tryAcquire(int arg)(Sync 是 ReentrantLock 的 AQS 子类)

protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 没有线程占用,尝试拿下(公平模式会检查队列,但 Sync 的非公平实现由 NonfairSync.lock 先用 CAS)
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    } else if (current == getExclusiveOwnerThread()) {
        // 可重入:当前线程已经是 owner,state 累加
        int nextc = c + acquires;
        setState(nextc);
        return true;
    }
    return false;
}

解释

  • state 表示锁的持有计数(重入次数)。
  • state == 0,试图把 state 设置为 acquires(1),并把当前线程设为 owner。
  • 若当前线程已经是 owner,则允许重入(state += acquires)。
  • 否则返回 false 表示不能立即获得锁,进入队列等待。

5) AbstractQueuedSynchronizer#addWaiter(int mode)(把当前线程封装成 Node 并尝试尾插 - 快速路径)

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // 快速尾插尝试
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    // 快速路径失败走慢路径
    return enq(node);
}

解释

  • 新建代表当前线程的 Node
  • 先尝试用 CAS 快速把 node 插入队尾(大多数情况下成功)。
  • 快速路径失败(或队列未初始化)则调用 enq(node)(慢路径,自旋直到入队成功)。

6) AbstractQueuedSynchronizer#enq(Node node)(慢路径,保证入队)

private Node enq(final Node node) {
    for (;;) {
        Node t = tail;
        if (t == null) {                // 如果队列尚未初始化
            if (compareAndSetHead(new Node()))
                tail = head;
        } else {
            node.prev = t;
            if (compareAndSetTail(t, node)) { // CAS 成功则把 t.next 指向 node
                t.next = node;
                return node;
            }
        }
    }
}

解释

  • 自旋直到插入成功。若队列尚未建立,会先创建虚拟 head,并把 tail = head,然后继续尾插。
  • tail 是队尾权威指针,用 CAS 设尾,随后设置 t.next 连接链表。

7) AbstractQueuedSynchronizer#acquireQueued(Node node, int arg)(等待循环)

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

解释(重点)

  • 循环体:在队列中轮询自己的前驱 p
  • 仅当 p == head(即自己是队首)时才试 tryAcquire(arg),以保证 FIFO 顺序(队首优先)。
  • shouldParkAfterFailedAcquire 用于决定并协商是否可以安全地挂起(park)当前线程(详见后文)。
  • parkAndCheckInterrupt 真正执行 LockSupport.park 并返回是否被中断。
  • 成功获得锁后通过 setHead(node) 把自己设为 head(并清理 thread 引用),返回是否曾被中断过。
  • 若异常/退出且未成功,会调用 cancelAcquire(node) 清理队列中的节点。

8) shouldParkAfterFailedAcquire(Node pred, Node node)

简化逻辑:

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true; // 已经被标记为 SIGNAL,说明 pred 在释放时会唤醒后继
    if (ws > 0) {
        // 前驱已取消,跳过被取消节点
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        // 尝试把 pred.waitStatus 设为 SIGNAL,告知 pred 在释放时唤醒后继
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

解释

  • 保证在 park 之前,前驱是处于 SIGNAL(会唤醒后继)的状态;如前驱是 CANCELLED,则跳过;若为 0,则尝试设置为 SIGNAL 后再下一轮 park。

9) parkAndCheckInterrupt()

private final boolean parkAndCheckInterrupt() {
    LockSupport.park(this);
    return Thread.interrupted();
}

解释

  • 使用 LockSupport.park 将线程挂起,直到被 unpark 或被中断。
  • Thread.interrupted() 返回并清除中断状态,acquireQueued 根据结果记录是否发生过中断(但对于不可中断的 acquire 只是记录并继续等待,最后返回让上层决定是否恢复中断)。

10) AbstractQueuedSynchronizer#setHead(Node node)

private void setHead(Node node) {
    head = node;
    node.thread = null;
    node.prev = null;
}

解释

  • 把当前已获取同步状态的节点设为 head,清理 thread、prev 避免内存泄漏;head 表示“队列的头(正在持有或刚获得资源的节点)”。

C. unlock() 的调用链(释放锁与唤醒后继)

11) ReentrantLock#unlock()

public void unlock() {
    sync.release(1);
}

解释:调用 AQS 的 release(1) 来释放一次持有计数(arg = 1),若 state 减到 0,表示当前线程完全释放锁,AQS 会唤醒队列中的下一个等待者。


12) AbstractQueuedSynchronizer#release(int arg)(模板方法)

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

解释

  • tryRelease(arg) 是由子类(ReentrantLock.Sync)实现,用于真正地改变 state 并在必要时返回 true 表示“完全释放”。
  • 若完全释放且队列头存在且 head.waitStatus != 0,则唤醒后继(unparkSuccessor(head)),这样队首线程会被唤醒去尝试获取锁。

13) ReentrantLock.Sync#tryRelease(int releases)(子类实现)

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

解释

  • 检查当前线程是否是 owner,否则抛异常。
  • statereleases(1)。若减到 0,表示锁完全释放,把 owner 清空并返回 true,AQS 的 release 将基于此唤醒后继。

14) AbstractQueuedSynchronizer#unparkSuccessor(Node node)

关键逻辑(简化):

private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0); // 清理 head 的 waitStatus
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        // 从 tail 向前找到未取消的候选
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}

解释

  • 找到 node(通常是 head)的后继 s(如果 node.next 被取消或为空,则从 tail 向前查找最近的非取消节点),然后对该节点的线程调用 LockSupport.unpark 唤醒它。
  • unpark 将让等待在 LockSupport.park 的线程返回,从而在 acquireQueued 循环中再次尝试 tryAcquire

D. 补充:常用的辅助函数(涉及并发语义但未全文列出)

  • compareAndSetState(int expect, int update):底层 Unsafe.compareAndSwapInt,用于 CAS 修改 state
  • compareAndSetTail(Node expect, Node update)compareAndSetHead(Node update):CAS 修改 tail/head
  • cancelAcquire(node):当线程在等待中被中断或超时、或方法出现异常,需要把节点从队列中清理的复杂逻辑(包括将该节点标记 CANCELLED 并修剪链表)。

E. 以一个典型执行场景串在一起(文字版执行顺序回顾)

当线程 T 调 lock.lock()(非公平)时:

  1. ReentrantLock.lock()NonfairSync.lock()
  2. 尝试 compareAndSetState(0,1):如果成功,setExclusiveOwnerThread(T) 并返回(拿到锁)。
  3. 如果 CAS 失败,进入 acquire(1)
  4. acquire(1) 内先调用 tryAcquire(1)(Sync.tryAcquire),失败则 addWaiter(Node.EXCLUSIVE)
  5. addWaiter 快速路径 CAS 插入 tail(或enq(node)慢路径自旋),返回新节点。
  6. acquireQueued(node, 1):循环检查 node.predecessor() 是否为 headtryAcquire(1) 成功;否则使用 shouldParkAfterFailedAcquire 协商并 park(阻塞)。
  7. 当前持有锁的线程调用 unlock() 时:sync.release(1)tryRelease(1) 更新 state,并在彻底释放(state==0)时调用 unparkSuccessor(head)
  8. unparkSuccessor 找到队首后继并 LockSupport.unpark(thread) 唤醒它。
  9. 被唤醒的线程在 parkAndCheckInterrupt() 返回,循环再次检查 p == head && tryAcquire(1),若成功则 setHead(node) 并退出,成为新的持有者。

F. 关键点与易错理解(总结要点)

  • state 含义:在 ReentrantLock 中表示当前持有锁的重入计数,arg 通常是 1
  • 快速路径 vs 慢路径:非公平锁优先用 CAS 快速获得锁;若失败则入队(可能又有快速 tail 插入)。
  • 队列保证公平性acquireQueued 只有在 predecessor == head 时才调用 tryAcquire,从而保证队列 FIFO 的“唤醒顺序”。
  • park/unpark 协议:通过 waitStatusSIGNAL 标志和 shouldParkAfterFailedAcquire 的状态转换,防止“挂起后无人唤醒”或“唤醒丢失”的 race。
  • 中断处理:不可中断的 acquire 在等待期间若被中断仅记录,最终获得锁后再恢复中断;可中断版本会在中断时抛出 InterruptedException 并清理队列。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐