AbstractQueuedSynchronizer 笔记

简介: AbstractQueuedSynchronizer 笔记

AbstractQueuedSynchronizer

AQS是一个内部维护了先进先出队列+标识(数字)的模型,标识使用CAS模式修改,作为多线程工具的基础组件

AQS队列结构.png

属性

属性名 说明
volatile Node head 为头节点会清理Node中关联的pre,thread避免GC不回收
volatile Node tail 尾节点
volatile int state 0为空闲其它组件按需使用,使用cas来赋值,
Thread exclusiveOwnerThread 持有线程

state为volatile的int,不同的业务场景按需实现,

独占模式:

ReentrantLock.Sync中state为0表示未锁定>0表示被几个线程持有

ThreadPoolExecutor.Worker中state为0表示未执行

共享模式:

CountDownLatch.Sync中state为初始化时指定,表示有多少个线程可持有,

Semaphore.Sync中state与CountDownLatch相同

混合模式:

ReentrantReadWriteLock.Sync中state为共享锁+独占锁组合 通过位运算16位来分割,最大的读锁写锁个数为65535

关键方法

独占模式

持有资源:acquire

acquire: 独占模式获取,独占模式即只有一个线程可更新state.忽略中断标识,在获取之后响应中断。

acquireInterruptibly:独占模式获取,线程标识为中断则抛出异常

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

AQS-acquire.png

tryAcquire:子类按需实现,使用独占模式更新state,增加state,成功返回true失败返回false

中断后不会正确响应park,所以需要重置线程中断标识,并在unpark之后进行中断补偿

释放资源:release

release:以独占模式释放资源

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

AQS-release.png

tryRelease:子类按需实现,使用独占模式更新state,减少state,并处理对应独占线程

共享模式

持有资源:acquireShared

  • acquireShared 共享模式获取,忽略中断线程,在获取之后相应中断。
  • acquireSharedInterruptibly 共享模式获取,响应中断,线程中断则抛出异常。
public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

AQS-acquireShared.png

tryAcquireShared:子类按需实现,对返回值有如下要求:

负值:失败。 零:共享模式下的获取成功,但是没有后续共享模式下的获取可以成功。 正值: 如果共享模式下的获取成功并且后续共享模式下的获取也可能成功,则为正值,在这种情况下,后续的等待线程必须检查可用性。 (对三个不同返回值的支持使该方法可以在仅有时进行获取的情况下使用。)成功后,就已经获取了此对象。

释放资源:releaseShared

  • releaseShared:以共享模式释放资源
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

AQS-releaseShared.png

tryReleaseShared:子类按需实现,使用共享模式更新state,减少state

其它关键方法

检查并更新节点状态:shouldParkAfterFailedAcquire

在park线程之前的判断,当前置节点为取消时更新前置节点

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

唤醒后续节点:unparkSuccessor

唤醒后续节点,并清理取消的节点,

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);
    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    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);
}

共享模式设置队列头:setHeadAndPropagate

共享模式下多个线程同时持有资源,头节点会频繁变化需要及时释放资源

private void setHeadAndPropagate(Node node, int propagate) {
    Node h = head; // Record old head for check below
    setHead(node);
    /*
     * Try to signal next queued node if:
     *   Propagation was indicated by caller,
     *     or was recorded (as h.waitStatus either before
     *     or after setHead) by a previous operation
     *     (note: this uses sign-check of waitStatus because
     *      PROPAGATE status may transition to SIGNAL.)
     * and
     *   The next node is waiting in shared mode,
     *     or we don't know, because it appears null
     *
     * The conservatism in both of these checks may cause
     * unnecessary wake-ups, but only when there are multiple
     * racing acquires/releases, so most need signals now or soon
     * anyway.
     */
    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            doReleaseShared();
    }
}

Node

属性名 说明
int waitStatus 1:CANCELLED,表示当前的线程被取消;<br/>-1:SIGNAL,后续节点需要unpark;<br/>-2:CONDITION,表示当前节点在等待condition,也就是在condition队列中;<br/>-3:PROPAGATE,A releaseShared应该传播到其他节点。 在doReleaseShared中对此进行了设置(仅适用于头节点),以确保传播继续进行,即使此后进行了其他操作也是如此。 <br/> 0:表示当前节点在sync队列中,等待着获取锁。
Node prev 前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接。
Node next 后继节点。
Thread thread 入队列时的当前线程。
Node nextWaiter 为NULL表示为独占模式

PROPAGATE:共享模式中会通过状态是否小于0来判断是否需要唤醒后续节点,共享模式下多个线程可同时持有state变更,waitStatus会频繁从0切换为SIGNAL,区分SIGNAL增加的中间状态所以称为传播值

目录
相关文章
|
7月前
|
弹性计算 人工智能 数据库
2026年阿里云服务器租用活动价格:1年、2年、3年可选实例及具体价格对比
2026年阿里云服务器活动价格更新,保留38元、99元与199元特价云服务器,并新增轻量应用服务器2核4G配置9.9元1月、199元1年抢购活动。活动推出多类实例的1年专属套餐,经济型e实例开放1年、2年、3年购买周期。本文详细解析了各实例在不同周期下的价格,并介绍了热门实例规格性能与适用场景,助力用户精准匹配最优云服务器实例规格、配置及购买时长。用户可根据业务需求及预算选择合适的实例规格和时长。
1214 11
|
4月前
|
机器学习/深度学习 人工智能 安全
从模型、Agent 到 MCP:这个 10.7k Star 项目,把 AI 工程学习路线重新铺了一遍
`ai-engineering-from-scratch` 是一套硬核AI工程实战路线:20阶段、435课、320小时,从数学原理手写实现Tokenizer/Attention/Backprop,到LLM微调、RAG、Agent、MCP及生产部署,每课产出可集成的Prompt/Skill/Agent等工程资产,专为构建可验证、可复现、可维护的AI系统而设计。
|
8月前
|
人工智能 API
这几天的大模型圈,真的有点“卷”过头了
科技圈正经历“模型春节”:DeepSeek推100万Token上下文与2025年知识,阿里千问Qwen3.5-Plus以小参数碾压大模型、多模态+低价引爆落地;Claude Sonnet 4.6稳准省心。模型不再唯一,适配场景才是关键——红利已至,会用者先赢。(239字)
400 3
|
敏捷开发 Dubbo Java
需求开发人日评估
敏捷开发中,工时评估是关键环节,常用“人日”衡量任务工作量。本文介绍人日概念及评估方法,涵盖开发、自测、联调、测试、发布各阶段周期参考,并提供常见需求的人日示例,助力团队更科学地制定计划。
749 0
|
机器学习/深度学习 人工智能 自然语言处理
《AI重塑网络开发:用户界面设计的革新之路》
在数字化浪潮中,人工智能(AI)正深刻变革网络开发的用户界面(UI)设计。AI通过个性化定制、自然语言处理、自动化设计和智能布局等技术,为每位用户带来独特的体验。借助AI分析用户行为,实现精准推荐;语音交互让操作更便捷;自动化工具加快设计流程;自适应设计确保跨设备一致性。AI驱动的UI设计不仅提升了用户体验,还为开发者提供了更多创新可能。
629 12
|
Web App开发 缓存 Windows
Windows / Visual Studio / Qt 实用快捷键 (持续更新)
Windows 平台,集成开发环境 Visual Studio 和 Qt 的一些使用键盘的便捷操作
851 0
|
数据可视化 搜索推荐 数据挖掘
基于Python flask 的数据可视化平台,可定制,可连接数据库
本文介绍了一个基于Python Flask框架开发的可定制数据可视化平台,该平台支持多种数据库连接,并提供丰富的图表类型和个性化设置,以实现交互式数据分析和展示。
658 0
基于Python flask 的数据可视化平台,可定制,可连接数据库
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp微信小程序的电影院订票选座系统的详细设计和实现
基于SpringBoot+Vue+uniapp微信小程序的电影院订票选座系统的详细设计和实现
431 1
|
缓存 前端开发 Java
15:Servlet 3.0文件上传与下载-Java Web
15:Servlet 3.0文件上传与下载-Java Web
634 5
|
easyexcel Java Maven
springboot使用EasyExcel导入导出填充,解决导出乱码问题(web)
springboot使用EasyExcel导入导出填充,解决导出乱码问题(web)
2659 5

热门文章

最新文章