设为首页 加入收藏

TOP

10分钟从源码级别搞懂AQS(AbstractQueuedSynchronizer)(七)
2023-09-09 10:25:55 】 浏览:145
Tags:10分 钟从源 别搞懂 AQS AbstractQueuedSynchronizer
bsp;      throw new InterruptedException();            //添加到末尾 不需要保证原子性,因为能指向await一定是获取到同步资源的            Node node = addConditionWaiter();            //释放获取的同步状态            int savedState = fullyRelease(node);            int interruptMode = 0;            //不在同步队列就park进入等待            while (!isOnSyncQueue(node)) {                LockSupport.park(this);                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)                    break;           }            //被唤醒后自旋获取同步状态            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)                interruptMode = REINTERRUPT;            //取消后清理            if (node.nextWaiter != null) // clean up if cancelled                unlinkCancelledWaiters();            if (interruptMode != 0)                reportInterruptAfterWait(interruptMode);       }

await主要将节点添加到condition object末尾,释放获取的同步状态,进入等待,唤醒后自旋获取同步状态

signal的主要逻辑在transferForSignal中

    final boolean transferForSignal(Node node) {
        //CAS修改节点状态 失败返回 变成取消
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;
        //加入AQS末尾
        Node p = enq(node);
        int ws = p.waitStatus;
        //CAS将节点状态修改为SIGNAL 成功则唤醒节点
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }

signal 主要把状态从-2condition 修改为 0(失败则取消节点), 然后加入AQS的末尾,最后再将状态该为-1 signal,成功则唤醒节点

为什么加入AQS末尾还是使用enq去CAS+失败重试操作保证原子性呢?

因为ConditionObject允许有多个,也就一个AQS同步队列可能对应多个Condition等待(条件)队列

image.png

总结

本篇文章以AQS为核心,深入浅出的描述AQS实现的数据结构、设计思想、获取/释放同步资源的源码级流程、Condition等

AQS使用头尾节点来实现双向队列,提供同步状态和获取/释放同步状态的模板方法来实现阻塞(同步)队列,并且这些字段使用volatile修饰,保证可见性与读取的场景配合,不需要保证原子性,在写的场景下常用CAS保证原子性

AQS与Condition使用相同类型的节点,在AQS中节点维护成双向链表,在Condition中节点维护成单向链表,节点除了维护指向关系,还需要记录对应线程和节点状态

AQS分为独占式和共享式,使用独占式时只允许一个线程获取同步状态,使用共享式时则允许多个线程获取同步状态;其中还提供响应中断、等待超时的类似方法

获取同步状态:先尝试获取同步状态,如果失败则CAS+失败重试的方式将节点添加到AQS末尾,等待被前驱节点唤醒;只有当前驱节点为头节点并且获取同步状态成功才返回,否则进入等待,被唤醒后继续尝试(自旋);在此期间如果发生异常,在抛出异常前会取消该节点

释放同步状态:尝试释放同步状态,成功后唤醒后继未被取消的节点

在获取同步状态时,被唤醒后会检查中断标识,如果是响应中断的则会直接抛出中断异常,不响应的则是在最外层自己中断

响应超时时,在自旋获取同步状态期间会计时,如果距离超时小于1ms就不进入等待的自旋,大于则再等待对应时间

AQS充当阻塞队列,Condition充当它的等待队列来实现等待/通知模式,AQS的内部类ConditionObject在await时会加入Condition末尾并释放同步状态进入等待队列,在被唤醒后自旋(失败会进入等待)获取同步状态;在single时会CAS的将condition头节点并加入AQS尾部再去唤醒(因为一个AQS可能对应多个Condition因此要CAS保证原子性)

最后(不要白嫖,一键三连求求拉~)

本篇文章

首页 上一页 4 5 6 7 下一页 尾页 7/7/7
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Nacos 注册中心的设计原理:让你.. 下一篇电商类面试问题--01Elasticsearch..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目