AQS (AbstractQueuedSynchronizer) 中,这些方法涉及到同步的获取和排队机制,它们实现了类似于锁(Lock)和信号量(Semaphore)的功能。AQS 通过内部维护一个 FIFO 队列和一些节点来管理线程的同步。下面逐个解释这些方法的作用:


AQS 核心方法和源码

1. acquire(int arg)

  • 作用:尝试获取同步状态,如果失败,则加入队列并阻塞线程。
public final void acquire(int arg) {
    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {
        selfInterrupt();
    }
}
  • 流程

    1. 调用 tryAcquire(arg) 尝试获取锁。
    2. 如果获取失败,则调用 addWaiter() 将线程加入队列。
    3. 调用 acquireQueued() 进行排队等待。
    4. 如果等待过程中被中断,调用 selfInterrupt() 重新设置中断状态。

2. tryAcquire(int arg)

  • 作用:尝试获取同步状态。这个方法是抽象方法,子类需要实现。
protected boolean tryAcquire(int arg) {
    throw new UnsupportedOperationException();
}
  • 注意:具体的同步工具(如 ReentrantLock)会重写此方法来定义获取同步状态的逻辑。

3. addWaiter(Node.EXCLUSIVE)

  • 作用:将当前线程封装成一个 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;
        }
    }
    enq(node);
    return node;
}
  • 流程

    1. 封装当前线程为一个 Node 节点。
    2. 尝试将节点加入等待队列尾部,如果失败则调用 enq() 进行入队。

4. 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)) {
                t.next = node;
                return t;
            }
        }
    }
}
  • 流程

    1. 检查队列是否存在头节点,如果不存在,初始化。
    2. 使用 CAS 操作将新节点加入队列尾部,保证线程安全。

5. 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; // 帮助 GC
                failed = false;
                return interrupted;
            }
            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) {
                interrupted = true;
            }
        }
    } finally {
        if (failed) cancelAcquire(node);
    }
}
  • 流程

    1. 获取前驱节点。
    2. 如果前驱节点是头节点,则尝试获取同步状态。
    3. 如果获取失败,调用 shouldParkAfterFailedAcquire() 判断是否需要挂起线程。
    4. 挂起线程并检查中断状态。

6. 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;
}
  • 流程

    1. 调用 tryRelease(arg) 释放同步状态。
    2. 如果释放成功,则唤醒等待队列中的后继线程。

7. tryRelease(int arg)

  • 作用:尝试释放同步状态。这个方法是抽象方法,需要子类实现。
protected boolean tryRelease(int arg) {
    throw new UnsupportedOperationException();
}
  • 注意:子类重写此方法,定义释放逻辑。

8. unparkSuccessor(Node node)

  • 作用:唤醒等待队列中的下一个线程。
private void unparkSuccessor(Node node) {
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        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);
}
  • 流程

    1. 将当前节点的 waitStatus 重置。
    2. 找到下一个需要唤醒的线程节点。
    3. 使用 LockSupport.unpark() 唤醒线程。

9. shouldParkAfterFailedAcquire()

  • 作用:检查节点的状态,决定线程是否需要挂起。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        return true;
    if (ws > 0) {
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

总结核心方法

方法名作用
acquire(int arg)获取同步状态,失败则排队等待。
tryAcquire(int arg)尝试获取同步状态,子类实现。
addWaiter(Node.EXCLUSIVE)添加节点到等待队列末尾。
enq(Node node)使用 CAS 操作将节点加入队列末尾。
acquireQueued()线程在队列中等待,直到获取同步状态。
release(int arg)释放同步状态并唤醒等待线程。
tryRelease(int arg)尝试释放同步状态,子类实现。
unparkSuccessor(Node node)唤醒等待队列中的下一个线程。
shouldParkAfterFailedAcquire()判断线程是否需要挂起。

其他核心概念:

  1. Node 类:代表等待队列中的线程节点。
  2. 状态管理

    • waitStatus:节点状态,包括 SIGNAL(等待唤醒)和 CANCELLED 等。
    • headtail:管理等待队列的头尾节点。
  3. CAS 操作:确保节点入队等操作的线程安全。

这些方法共同配合,实现了 AQS 的底层同步控制机制,包括线程的排队、阻塞、唤醒等功能。


今夜有点儿凉
37 声望1 粉丝

今夜有点儿凉,乌云遮住了月亮。


« 上一篇
JVM 调优