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,禁止外部直接调用,确保所有访问都经过模板方法的排队策略,防止插队破坏语义。
相关推荐
亦暖筑序19 小时前
Java 8老系统AI Workflow实战:把一次性AI对话升级成可恢复工作流
java·后端
敲代码的彭于晏20 小时前
Bean 生命周期完全图解:前端同学也能看懂的 Spring 核心机制
java·前端·后端
plainGeekDev21 小时前
ButterKnife → ViewBinding
android·java·kotlin
像我这样帅的人丶你还2 天前
Java 后端详解(四):分页与搜索
java·javascript·后端
她的男孩2 天前
数据权限为什么不能只靠注解?Forge 的 Mapper 层 SQL 改写源码拆解
java·后端·架构
tntxia2 天前
Mybatis的日志输入
java
亦暖筑序2 天前
Java 8老系统Browser Agent实战:三层拦截把AI操作后台变成可审计流程
java·后端·设计模式
用户298698530142 天前
Java 实现 Word 文档加密与权限解除
java·后端
Yeats_Liao2 天前
14:Servlet中的页面跳转-Java Web
java·后端·架构
未秃头的程序猿2 天前
告别"if-else地狱"!Java 21模式匹配,代码优雅了10倍
java·后端·面试