AQS、Condition

目录

  • 一、AQS抽象类
    • 1.自定义AQS
    • 2.如何实现多个线程按序执行
    • 3.独占锁
      • 3.1 acquire()方法(ReentrantLock源码为例)
      • 3.2 release()方法(ReentrantLock源码为例)
    • 4.共享锁
      • 4.1 acquireShared()方法(Semaphore源码为例)
      • 4.2 releaseShared()方法(Semaphore源码为例)
  • 二、Condition

一、AQS抽象类

CLH锁获取锁失败时仍然会自旋,性能差。

AQS维护双向队列记录等待的线程,head指针持有锁的线程、tail指针指向队列尾部用于插入新的线程、state字段用来记录锁的使用情况,具体如何使用由实现类自定义ReentrantLock: state表示锁的重入次数、Semaphore: state表示可用许可数量、CountDownLatch: state表示剩余计数)。

线程首先CAS修改tail指针加入队列,然后分别CAS修改自己的前向指针和前结点的后向指针。与CAS不同的是,将获取锁失败的自旋操作替换为主动调用LockSupport.park()放弃CPU进入WATTING状态,head结点在释放锁后根据后向指针调用LockSupport.unpark()唤醒后继节点,后继结点获取锁后将自己的前向指针置空,便于垃圾回收器回收,然后开始获取任务,后向指针由后继结点负责连接。

上述结点和指针都要加volatile确保可见性。

1.自定义AQS

AQS是一个抽象类,定义了资源获取和释放的通用框架(双向队列+state+head、tail+等待唤醒机制),而具体的资源获取逻辑需要重写模板方法来实现。

需要定义的资源获取逻辑(重写的模版方法):

//独占方式。尝试获取资源,成功则返回true,失败则返回false。protectedbooleantryAcquire(int)//独占方式。尝试释放资源,成功则返回true,失败则返回false。protectedbooleantryRelease(int)//共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。protectedinttryAcquireShared(int)//共享方式。尝试释放资源,成功则返回true,失败则返回false。protectedbooleantryReleaseShared(int)//该线程是否正在独占资源。只有用到condition才需要去实现它。protectedbooleanisHeldExclusively()

自定义AQS感觉可以实现多个线程有序执行的业务,每个线程持有不同的id号,所有线程CAS自旋volatile state,仅当state为时获取锁,每个线程执行完修改state为下一个要执行的线程的state,然后释放锁。不过上述也没有用AQS,现在还想不出来怎么用AQS实现,主要是对这个抽象类的代码太陌生了。

2.如何实现多个线程按序执行

前两天看的面试题,今天突然就有思路了。

  1. CAS:实现多个线程有序执行的业务,每个线程持有不同的id号,所有线程CAS自旋volatile state,仅当state为时获取锁,每个线程执行完修改state为下一个要执行的线程的state,然后释放锁。
  2. AQS:因为CAS自旋太占CPU,将所有线程乱序入队head→T1→T3→T2,首先对首T1获取锁执行完,唤醒T3,但是T3CAS发现state跟自己序号不一样,所以实例化一个新的结点,将自己加入队尾,释放锁,唤醒T2,T2执行完唤醒T3。

3.独占锁

ReentrantLock就是AQS的独占锁实现类,定义state字段含义为锁的重入次数仅当state==0时表示当前没有线程持有锁state>0表示锁被占用,同时基于state实现了可重入机制,state--至0时释放锁

3.1 acquire()方法(ReentrantLock源码为例)

AQS本身不提供公平模式和非公平模式的实现,而是由实现类在模版方法中自行编写需要的模式,ReentrantLock实现了两种模式,下面介绍非公平模式:

  • 非公平模式:当一个线程尝试获取锁时,它会直接尝试获取,而不管等待队列中是否有其他线程在等待。如果获取失败,它才会加入等待队列

  • 公平模式:当一个线程尝试获取锁(或信号量)时,它会先检查等待队列中是否有其他线程在等待,如果有,那么它就会直接进入等待队列,而不是尝试获取。

该方法用于获取资源,会调用自定义的模版方法:

  • tryAcquire():首先获取state判断state==0,那么表示锁未被使用,那么尝试CAS修改state=state+1获取锁(仅非公平模式下执行,公平模式下直接进去等待队列)。
  • addWaiter():获取锁失败后,将当前线程封装为结点,CAS更新尾指针tail加入双向队列中。如果有前驱结点,还需要CAS修改前驱结点的next指针指向当前结点,指定前驱结点释放锁后要唤醒哪个结点
  • acquireQueued():当前线程加入队列之后,如果发现head指向当前节点,说明当前线程是队列中第一个等待的节点,于是调用tryAcquire()尝试获取锁。如果尝试获取锁失败或不是第一个节点,当前结点调用LockSupport.park()进入等待状态,等待被唤醒
publicfinalvoidacquire(intarg){if(!tryAcquire(arg)&&acquireQueued(addWaiter(Node.EXCLUSIVE),arg))selfInterrupt();}protectedbooleantryAcquire(intacquires){finalThreadcurrent=Thread.currentThread();// 1、获取 AQS 中的 state 状态intc=getState();// 2、如果 state 为 0,证明锁没有被其他线程占用if(c==0){// 2.1、通过 CAS 对 state 进行更新if(compareAndSetState(0,acquires)){// 2.2、如果 CAS 更新成功,就将锁的持有者设置为当前线程setExclusiveOwnerThread(current);returntrue;}}// 3、如果当前线程和锁的持有线程相同,说明发生了「锁的重入」elseif(current==getExclusiveOwnerThread()){intnextc=c+acquires;if(nextc<0)// overflowthrownewError("Maximum lock count exceeded");// 3.1、将锁的重入次数加 1setState(nextc);returntrue;}// 4、如果锁被其他线程占用,就返回 false,表示获取锁失败returnfalse;}privateNodeaddWaiter(Nodemode){// 1、将当前线程封装为 Node 节点。Nodenode=newNode(Thread.currentThread(),mode);Nodepred=tail;// 2、如果 pred != null,则证明 tail 节点已经被初始化,直接将 Node 节点加入队列即可。if(pred!=null){node.prev=pred;// 2.1、通过 CAS 控制并发安全。if(compareAndSetTail(pred,node)){pred.next=node;returnnode;}}// 3、初始化队列,并将新创建的 Node 节点加入队列。enq(node);returnnode;}finalbooleanacquireQueued(finalNodenode,intarg){booleanfailed=true;try{booleaninterrupted=false;for(;;){// 1、尝试获取锁。finalNodep=node.predecessor();if(p==head&&tryAcquire(arg)){setHead(node);p.next=null;// help GCfailed=false;returninterrupted;}// 2、判断线程是否可以阻塞,如果可以,则阻塞当前线程。if(shouldParkAfterFailedAcquire(p,node)&&parkAndCheckInterrupt())interrupted=true;}}finally{// 3、如果获取锁失败,就会取消获取锁,将节点状态更新为 CANCELLED。if(failed)cancelAcquire(node);}}

3.2 release()方法(ReentrantLock源码为例)

该方法用于释放资源,会调用自定义的模版方法:

  • tryRelease()CAS更新state=0释放锁,修改持有锁的线程为null。
  • unparkSuccessor():调用LockSupport.unpark()唤醒当前结点的后向指针指向的结点。
publicfinalbooleanrelease(intarg){// 1、尝试释放锁if(tryRelease(arg)){Nodeh=head;// 2、唤醒后继节点if(h!=null&&h.waitStatus!=0)unparkSuccessor(h);returntrue;}returnfalse;}protectedfinalbooleantryRelease(intreleases){intc=getState()-releases;// 1、判断持有锁的线程是否为当前线程if(Thread.currentThread()!=getExclusiveOwnerThread())thrownewIllegalMonitorStateException();booleanfree=false;// 2、如果 state 为 0,则表明当前线程已经没有重入次数。因此将 free 更新为 true,表明该线程会释放锁。if(c==0){free=true;// 3、更新持有资源的线程为 nullsetExclusiveOwnerThread(null);}// 4、更新 state 值setState(c);returnfree;}// 这里的入参 node 为队列的头节点(虚拟头节点)privatevoidunparkSuccessor(Nodenode){intws=node.waitStatus;// 1、将头节点的状态进行清除,为后续的唤醒做准备。if(ws<0)compareAndSetWaitStatus(node,ws,0);Nodes=node.next;// 2、如果后继节点异常,则需要从 tail 向前遍历,找到正常状态的节点进行唤醒。if(s==null||s.waitStatus>0){s=null;for(Nodet=tail;t!=null&&t!=node;t=t.prev)if(t.waitStatus<=0)s=t;}if(s!=null)// 3、唤醒后继节点LockSupport.unpark(s.thread);}

4.共享锁

Semaphore就是AQS的共享锁实现类,可实现多线程同时持有锁,定义state字段含义为锁的剩余容量仅当state==0时表示当前锁已没有余量state>0表示锁被占用,但仍有余量可申请。

4.1 acquireShared()方法(Semaphore源码为例)

该方法用于获取资源,会调用自定义的模版方法,默认使用自定义的非公平模式

  • tryAcquireShared():首先获取state判断state>0,那么表示锁还有剩余,那么尝试CAS修改state=state-1获取锁(仅非公平模式下执行,公平模式下直接进去等待队列)。
  • doAcquireShared():获取锁失败后,将当前线程封装为结点,CAS更新尾指针tail加入双向队列中。如果有前驱结点,还需要CAS修改前驱结点的next指针指向当前结点,指定前驱结点释放锁后要唤醒哪个结点。如果head指向当前节点,说明当前线程是队列中第一个等待的节点,于是调用tryAcquire()尝试获取锁,如果锁仍有余量那么同时唤醒后继结点如果尝试获取锁失败或不是第一个节点,当前结点调用LockSupport.park()进入等待状态,等待资源有余量被唤醒
publicfinalvoidacquireShared(intarg){if(tryAcquireShared(arg)<0)doAcquireShared(arg);}finalinttryAcquireShared(intacquires){for(;;){// 1、获取可用资源数量。intavailable=getState();// 2、计算剩余资源数量。intremaining=available-acquires;// 3、如果剩余资源数量 < 0,则说明资源不足,直接返回;如果 CAS 更新 state 成功,则说明当前线程获取到了共享资源,直接返回。if(remaining<0||compareAndSetState(available,remaining))returnremaining;}}privatevoiddoAcquireShared(intarg){// 1、将当前线程加入到队列中等待。finalNodenode=addWaiter(Node.SHARED);booleanfailed=true;try{booleaninterrupted=false;for(;;){finalNodep=node.predecessor();if(p==head){// 2、如果当前线程是等待队列的第一个节点,则尝试获取资源。intr=tryAcquireShared(arg);if(r>=0){// 3、将当前线程节点移出等待队列,并唤醒后续线程节点。setHeadAndPropagate(node,r);p.next=null;// help GCif(interrupted)selfInterrupt();failed=false;return;}}if(shouldParkAfterFailedAcquire(p,node)&&parkAndCheckInterrupt())interrupted=true;}}finally{// 3、如果获取资源失败,就会取消获取资源,将节点状态更新为 CANCELLED。if(failed)cancelAcquire(node);}}

4.2 releaseShared()方法(Semaphore源码为例)

该方法用于释放资源,会调用自定义的模版方法:

  • tryReleaseShared()CAS更新state++释放锁
  • doReleaseShared():调用LockSupport.unpark()唤醒当前结点的后向指针指向的结点。
publicfinalbooleanreleaseShared(intarg){if(tryReleaseShared(arg)){doReleaseShared();returntrue;}returnfalse;}protectedfinalbooleantryReleaseShared(intreleases){for(;;){intcurrent=getState();intnext=current+releases;if(next<current)// overflowthrownewError("Maximum permit count exceeded");if(compareAndSetState(current,next))returntrue;}}

二、Condition

Object类的wait()/notify()依赖于synchronized锁实现了线程间的等待和通知机制,必须在synchronized代码块内调用,且调用notify()会唤醒全部wait()在Object对象上的线程。

Condition依赖ReentrantLock锁,底层实现的是park/unpark机制,它的优点是对于每个ReentrantLock对象可以设置多个Condition,线程可以根据任务要求Condition.await()在ReentrantLock对象不同的Condition上,唤醒线程时调用对应Condition.signal()实现部分唤醒。

  • await():进入Condition等待队列等待被唤醒线程会唤醒后先获取ReentrantLock然后执行后的代码
  • signal()将Condition等待队列中的线程全部唤醒
publicclassReentrantLockDemo{staticReentrantLocklock=newReentrantLock();staticintstate=0;publicstaticvoidmain(String[]args){// 一个锁对象可以创建多个ConditionConditionnotEmpty=lock.newCondition();ConditionnotFull=lock.newCondition();newThread(()->{while(true){lock.lock();try{// await会自动释放锁,线程被唤醒并执行后续代码前会先获取锁if(state==0)notEmpty.await();state--;System.out.println("消费一次");notFull.signal();}catch(Exceptione){e.printStackTrace();}finally{lock.unlock();}}}).start();newThread(()->{while(true){lock.lock();try{// await会自动释放锁,线程被唤醒并执行后续代码前会先获取锁if(state==1)notFull.await();state++;System.out.println("生产一次");notEmpty.signal();}catch(Exceptione){e.printStackTrace();}finally{lock.unlock();}}}).start();}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1184368.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

震惊!大模型推理技术天花板揭秘:从“内存墙“到“算力突围“,小白也能秒懂的AI开发进阶指南

1. 介绍&#xff1a;计算范式与推理架构的演进 随着人工智能领域进入以生成式预训练变换器&#xff08;Generative Pre-trained Transformer, GPT&#xff09;为代表的大模型时代&#xff0c;模型参数规模从数十亿&#xff08;Billion&#xff09;级别迅速攀升至万亿&#xff…

写论文好用的AI:从辅助构思到质量控制的智能伙伴

在学术研究领域&#xff0c;人工智能已从遥远的概念演进为触手可及的现实助手。对于论文作者而言&#xff0c;“好用的AI”绝非指能够一键生成全文的替代工具&#xff0c;而是指那些能够嵌入研究全流程、切实提升效率与质量、且符合学术伦理的智能伙伴。这些工具能够在文献迷雾…

大模型开发者的福音:一文搞懂Agent评估,让你的模型不再“翻车“!

一、Agent 评估为什么这么重要 LLM 的输出是存在不可控因素的&#xff0c;而对于一个线上生产级别的大模型应用来说&#xff0c;稳定性是最重要的&#xff0c;成熟的评估方案不仅可以让大模型应用更加稳定&#xff0c;同时也可以发现模型的潜力和边界&#xff0c;以此更好的迭…

Matlab: 测试MMS (Method of Manufactured Solutions)

使用泊松方程测试MMS(Method of Manufactured Solutions)方法适用性 % % TEST MMS (Method of Manufactured Solutions) %function poissonMMS()%% ucn*(x-x0)-sn*(y-y0)% vsn*(x-x0)cn*(y-y0)% T(x,y)exp(-au^2-bv^2)%thetapi/4;p.a10;p.b100;p.cncos(theta);p.snsin(theta);p…

在3D设计课上,国产CAD兼顾入门与实战

我在职校教3D设计课&#xff0c;真的很头疼用哪个牌子的软件。有些孩子基础很差&#xff0c;对电脑操作很不敏感&#xff0c;不能用过于复杂的软件作为教学工具&#xff0c;但用的软件太简单又不能很好的衔接企业实际岗位标准&#xff0c;课就白上了。要选一款既要贴合学生的认…

【AI编程干货】2025大模型开发已从“随机生成“进化到“确定性工程“,这篇技术指南让你少走三年弯路!

01 宏观生态概览&#xff1a;从随机生成到确定性工程 1.1 2025年 AI 工程化的范式转移 在2023年至2025年的短短两年间&#xff0c;大语言模型&#xff08;LLM&#xff09;的工程生态经历了一场深刻的范式转移。如果说2023年是“聊天机器人&#xff08;Chatbot&#xff09;”…

2026年优秀的桥架支架,热浸锌桥架,电缆沟支架厂家采购优选榜单 - 品牌鉴赏师

引言在现代基础设施建设中,电缆支架、隧道支架、电缆沟支架、管廊支架、热浸锌桥架以及桥架支架等产品扮演着至关重要的角色。它们的质量和性能直接关系到电缆系统的安全与稳定运行。为了帮助广大采购商在众多厂家中挑…

大模型开发必看!LangChain 1.0 MCP调用实战,解决DeepSeek兼容性问题,附完整代码

上一篇文章中&#xff0c;我为大家介绍了LangChain1.0框架下调用人机交互式大模型的方法。今天&#xff0c;我们聚焦另一个核心实操场景——MCP&#xff08;Model Context Protocol&#xff09;的调用流程&#xff0c;以及实践中常见报错的解决方案。 一、基础铺垫&#xff1a…

AI Agent架构大揭秘:从感知到行动,让代码拥有“大脑“!2026年AI开发必备技能,程序员速来围观!

AI Agent 是2026年AI生态的核心概念&#xff0c;它指的是一个具备自主决策、规划和执行能力的数字实体&#xff0c;不再局限于简单的问答或生成式AI&#xff0c;而是能像人类员工一样处理复杂任务。简单来说&#xff0c;Agent 能理解用户意图、分解目标成步骤、调用外部工具或数…

2026/1/19-又是会考

前天会考去了。 重要的不是会考,重要的是我要回学校【生无可恋.jpg】 但是能和 lzm 玩,感觉又活了一些。16 号下午去看考场,然后被打信息差了。 教室和办公室都搬到科技楼去了,我还以为办公室还在教学楼捏。 于是在…

程序员必看!RelayLLM:大模型“点餐“式协作,小模型精准求助,性能提升60%成本降低98%!

一、动机 当前大模型在复杂推理任务上表现出色&#xff0c;但计算成本和延迟让人望而却步&#xff1b;小模型虽然高效&#xff0c;但推理能力又明显不足 现有的协作方案&#xff08;如级联路由&#xff09;通常采用"全有或全无"的策略&#xff1a;路由器判断题目难…

【AI开发必备】大模型Agent评估全攻略:从编码到对话,保姆级教程让你少走90%弯路!

揭秘 AI 代理的评估 - 多种Agent的评估方法 前言&#xff1a; 在上一篇文章中&#xff0c;我们只是介绍了Agent评估常用的方法&#xff0c;没有具体的案例&#xff0c;本文中是具体的几种Agent类型的评估方法&#xff0c;编码Agent、研究搜索Agent、对话聊天Agent、计算机操作…

赋能企业健康服务升级—HealthAI开放平台的全链路技术解决方案

在数字化浪潮与健康需求升级的双重驱动下&#xff0c;健康管理正从传统模式向AI精准服务转型。企业对专业化、场景化的数字化健康管理产品需求日益迫切&#xff0c;健康有益HealthAI健康云开放平台以垂直领域深耕优势&#xff0c;为ToB客户提供全链路AI健康管理解决方案&#x…

【收藏必看】AI Agent核心组件深度解析:从记忆、工具到规划,构建智能体全攻略

文章详细介绍了AI Agent的定义与三大核心组件&#xff1a;记忆系统&#xff08;短期与长期记忆&#xff09;、工具调用&#xff08;与外部环境交互&#xff09;和规划能力&#xff08;任务分解与执行&#xff09;。通过这些组件&#xff0c;Agent能增强LLM能力&#xff0c;执行…

2026 天津线上培训班权威推荐榜:天津蔚然文化 9.98 分断层领跑,全场景提分首选 - 品牌智鉴榜

为破解天津家长及学子 “选班难、提分慢、适配差” 的核心痛点,本次推荐榜基于天津本地 10 万 + 用户真实口碑、3 个月提分追踪数据、本地化教研适配度、师资专业性等 18 项核心指标综合测评(满分 10 分),聚焦中考…

数控滑台稳定可靠:持续运行的坚实保障

数控滑台作为现代制造系统的关键执行单元&#xff0c;其稳定性与可靠性直接影响生产精度与效率。以下技术优势保障了其持续运行的稳定性&#xff1a;一、结构刚性优化采用高强度铸铁基座与精密直线导轨组合&#xff0c;实现基础结构刚度提升。通过有限元分析&#xff08;FEA&am…

项目管理工具——禅道

禅道的安装: 注意:禅道安装需要在全英文路径下安装使用!!!点击开源版:此状态为安装完成状态!

力扣热题100 11. 盛最多水的容器

前提提要&#xff1a;看懂题目很重要,看懂题目之后就很简单了&#xff0c;用简单的写法先写一遍&#xff0c;超时了然后换思路&#xff0c;如果不会赶紧看题解。题目出的跟数学题一样&#xff0c;忍不住爆粗口… 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;…

清华大学行人避让行为的动力学与运动学特征研究:基于高精度光学动作捕捉系统的实验分析

清华大学聂冰冰老师团队通过NOKOV度量动作捕捉系统,量化了行人避让行为的动力学和运动学特征,分析了行人与车辆在碰撞前的交互过程。实验中,行人在虚拟交通环境中执行避让行为,数据包括速度、加速度、关节角度等关…

试验台铁地板加工厂家:十字数控滑台安装与维护

好的&#xff0c;关于试验台铁地板加工厂家及十字数控滑台的安装与维护&#xff0c;以下是清晰的解答&#xff1a;1. 厂家选择标准选择试验台铁地板加工厂家时&#xff0c;需重点关注&#xff1a;加工精度&#xff1a;铁地板平面度需满足高精度要求&#xff08;例如平面度误差 …