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  阅读(91)  评论(0)    收藏  举报