AQS-钩子方法

钩子方法是 AQS 留给子类定义同步语义 的扩展点。它们全部声明为 protected,由模板方法在特定时机回调。以下对五个核心钩子方法逐一进行源码级剖析。


一、独占模式钩子

1. tryAcquire(int arg)

属性 说明
调用者 acquire / acquireInterruptibly / tryAcquireNanos
参数 arg:期望获取的资源数量(通常为 1)
返回值 true 表示获取成功;false 表示获取失败
副作用 成功时通常需要修改 state 并设置独占线程

实现要点

  • 必须非阻塞 ,通常用 if-else 判断并立即返回。
  • 修改 state必须使用 compareAndSetState(除重入场景外)。
  • 需正确处理重入:若当前线程已独占资源,则增加计数。

📝 ReentrantLock 非公平锁实现

java 复制代码
protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        // 无锁:CAS 抢占
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        // 重入:计数累加(无需 CAS,单线程安全)
        int nextc = c + acquires;
        if (nextc < 0) throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

2. tryRelease(int arg)

属性 说明
调用者 release
参数 arg:期望释放的资源数量(通常为 1)
返回值 true 表示资源已完全释放state == 0);false 表示仍持有资源
副作用 减少 state,完全释放时清除独占线程

实现要点

  • 该方法由持有锁的线程单线程调用 ,因此不需要 CAS ,直接用 setState
  • 需先检查调用者是否为持有者,否则抛出 IllegalMonitorStateException
  • 仅当 state 减至 0 时返回 true,触发 AQS 唤醒后继节点。

📝 ReentrantLock.Sync 实现

java 复制代码
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;
}

二、共享模式钩子

3. tryAcquireShared(int arg)

属性 说明
调用者 acquireShared / acquireSharedInterruptibly / tryAcquireSharedNanos
参数 arg:期望获取的资源数量
返回值 负值 :获取失败;0 或正值 :获取成功,数值表示获取后仍剩余的资源数
副作用 成功时 CAS 扣减 state

实现要点

  • 必须使用 CAS 循环 扣减 state
  • 返回值直接影响 AQS 是否进行链式唤醒 :若 > 0,AQS 会继续唤醒后续共享节点。

📝 Semaphore 非公平实现

java 复制代码
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 || compareAndSetState(available, remaining))
            return remaining;
    }
}

4. tryReleaseShared(int arg)

属性 说明
调用者 releaseShared
参数 arg:期望释放的资源数量
返回值 true 表示释放成功且状态已变更(可能从 0 变为正数),需要唤醒等待者
副作用 CAS 增加 state

实现要点

  • 必须使用 CAS 循环 增加 state,因为释放操作可能由多个线程并发执行。
  • 返回 true 时,AQS 会执行 doReleaseShared() 唤醒等待线程。

📝 Semaphore 实现

java 复制代码
protected final 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))
            return true;
    }
}

三、查询钩子

5. isHeldExclusively()

属性 说明
调用者 AQS 内部条件队列(ConditionObject)及诊断方法(如 hasQueuedThreads
返回值 true 表示当前线程独占持有同步器
副作用 仅查询,不修改状态

实现要点

  • 通常通过比较 getExclusiveOwnerThread()Thread.currentThread() 实现。
  • 用于 Condition.await() 前置检查:若未持有锁却调用 await,则抛出 IllegalMonitorStateException

📝 ReentrantLock.Sync 实现

java 复制代码
protected final boolean isHeldExclusively() {
    return getExclusiveOwnerThread() == Thread.currentThread();
}

四、钩子方法决策矩阵

钩子方法 模式 调用时机 核心问题 修改 state 是否 CAS
tryAcquire 独占 尝试获取锁时 现在能独占吗? 0 → 1 / 累加重入 是(重入除外)
tryRelease 独占 释放锁时 完全释放了吗? 递减,归零时清空 否(单线程)
tryAcquireShared 共享 尝试获取共享资源时 能获取吗?剩多少? 扣减
tryReleaseShared 共享 释放共享资源时 资源增加了吗? 增加
isHeldExclusively 独占 条件检查时 当前线程是持有者吗? 只读 ---

五、钩子方法与模板方法的交互时序

以共享模式获取为例,展示 tryAcquireShared 如何嵌入模板流程:

sequenceDiagram participant T as 线程 participant TM as acquireShared(模板) participant HK as tryAcquireShared(钩子) participant Q as CLH队列 T->>TM: acquireShared(1) TM->>HK: 1. tryAcquireShared(1) HK-->>TM: 返回 remaining alt remaining >= 0 TM-->>T: 直接成功 else remaining < 0 TM->>Q: 2. addWaiter(SHARED) loop 自旋 Q->>HK: 前驱为head时再次 tryAcquireShared HK-->>Q: remaining alt remaining >= 0 Q->>Q: setHeadAndPropagate (若remaining>0则继续唤醒) Q-->>T: 成功返回 else Q->>Q: park阻塞 end end end

六、设计钩子方法的目的再总结

  1. 分离变化与不变:将不可变的排队、阻塞、唤醒算法固定于模板方法,将可变的资源判定逻辑开放给钩子。
  2. 强制无锁化语义 :钩子方法不声明 synchronized,强制子类使用 CAS,保证整个 AQS 框架的无锁高性能特性。
  3. 最小化实现成本:开发者仅需 5~20 行代码实现钩子,即可获得一个功能完整的同步器。
  4. 防御性保护 :钩子为 protected,禁止外部直接调用,确保所有访问都经过模板方法的排队策略,防止插队破坏语义。
相关推荐
郭郭的柳柳在学FPGA2 小时前
千兆以太网@——帧格式
java·开发语言·网络
真恋寄语枫秋3 小时前
【Java零基础入门20】Java Stream流超全详解:中间操作、终结操作、集合数据处理
java
段ヤシ.3 小时前
回顾Java知识点,面试题汇总Day10(持续更新)
java·开发语言·spring
Dicky-_-zhang3 小时前
Elasticsearch聚合查询优化实战
java·jvm
淼淼爱喝水3 小时前
【Ansible 入门实战】三种变量详解
java·linux·数据库·ansible·playbook
Perry 1233 小时前
Java中的多态
java·开发语言
asdfg12589633 小时前
一文理解软件开发中的“设计模式”
java·设计模式·软件开发
hikktn3 小时前
企业级Spring Boot应用管理:从零打造生产级启动脚本
java·spring boot·后端