Semaphore.release()方法的底层原理

一、release() 方法代码解析

当调用 release() 方法时,实际调用的是 AQS 的 releaseShared(1) 方法。以下是其详细工作流程:

 public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
 }


1、Semaphore.release()

public void release() {
    sync.releaseShared(1); // 调用 AQS 的共享模式方法
}


2、AQS.releaseShared(int arg)

 public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) { // 尝试释放许可
        doReleaseShared(); // 唤醒等待线程
        return true;
    }
    return false;
}


3、Semaphore.tryReleaseShared(int releases)

protected boolean tryReleaseShared(int releases) {
    for (;;) {
        int current = getState(); // 当前剩余许可数
        int next = current + releases; // 计算新的许可数
        if (next < current) {
            throw new Error("Maximum permit count exceeded"); // 防止溢出
        }
        if (compareAndSetState(current, next)) { // CAS 更新 state
            return true; // 成功释放许可
        }
    }
}


4、AQS.doReleaseShared()

private void doReleaseShared() {
    for (;;) {
        Node h = head; // 获取队列头节点
        if (h != null && h != tail) { // 队列不为空
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) { // 需要唤醒后继节点
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) {
                    continue; // CAS 失败,重试
                }
                unparkSuccessor(h); // 唤醒后继节点
            } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {
                continue; // CAS 失败,重试
            }
        }
        if (h == head) { // 头节点未变化,退出循环
            break;
        }
    }
}


二、release() 的工作流程

当调用 release() 方法时,实际调用的是 AQS 的 releaseShared(1) 方法。以下是其详细工作流程:


(1)、增加许可数


1、调用 tryReleaseShared(arg):

  • tryReleaseShared(arg) 是 AQS 的模板方法,由 Semaphore 实现。

  • 在 Semaphore 中,tryReleaseShared(arg) 的逻辑是通过 int next = current + releases; 操作将 state 增加 1。


2、CAS 操作:

  • 使用 compareAndSetState(current, next) 方法原子性地更新 state 变量。

  • 如果 CAS 成功,表示线程成功释放许可;否则,重试。


(2)、唤醒等待线程


1、检查等待队列:

  • 如果 CLH 队列中有等待的线程,AQS 会唤醒队列中的第一个有效节点(线程)。

  • 被唤醒的线程会重新尝试获取许可。


2、传播唤醒:

  • 在共享模式下,AQS 会传播唤醒信号,确保所有等待的线程都有机会获取许可。
posted @ 2025-03-03 15:38  jock_javaEE  阅读(52)  评论(0)    收藏  举报