网站开发流程的8个步骤铜陵县住房和城乡建设局网站
2026/1/18 23:23:24 网站建设 项目流程
网站开发流程的8个步骤,铜陵县住房和城乡建设局网站,移动网站开发技术,Wordpress 报表的插件文章目录一、AQS的排队哲学#xff1a;线程如何优雅等待二、双向链表#xff1a;AQS的高效秘诀1. 高效处理中途离场#xff1a;线程取消的优雅解决方案2. 从轮询到唤醒#xff1a;性能的巨大飞跃3. 状态的高效传播#xff1a;共享模式…文章目录一、AQS的排队哲学线程如何优雅等待二、双向链表AQS的高效秘诀1. 高效处理中途离场线程取消的优雅解决方案2. 从轮询到唤醒性能的巨大飞跃3. 状态的高效传播共享模式的精髓三、实战分析ReentrantLock中的双向链表应用四、设计哲学的思考空间换时间的经典权衡五、总结参考文档大家好我是你们的技术老友科威舟今天给大家分享一下为什么AQS选择了双向链表。同步器设计的精髓就藏在线程排队的数据结构里在Java并发编程的世界里AbstractQueuedSynchronizerAQS堪称是同步器设计的脊柱。它隐藏在ReentrantLock、Semaphore、CountDownLatch等强大工具的背后默默协调着线程间的协作。而AQS核心中的核心就是那个神秘的等待队列。今天我们就来解开AQS选择双向链表而非单向链表背后的设计智慧。一、AQS的排队哲学线程如何优雅等待想象一下医院挂号的场景病人线程需要获取挂号资源state变量。如果资源充足病人直接获取如果号源紧张新来的病人就需要排队等待。AQS的等待队列就是管理这些排队线程的机制。它需要解决几个核心问题如何高效管理大量等待线程当有线程放弃等待时如何快速移除资源释放时如何准确唤醒下一个等待者AQS的解决方案是维护一个FIFO双向队列每个等待线程被封装成一个Node节点包含前驱prev和后继next指针。staticfinalclassNode{volatileNodeprev;volatileNodenext;volatileThreadthread;volatileintwaitStatus;// ...}但为什么是双向链表单向链表不够简单高效吗二、双向链表AQS的高效秘诀1. 高效处理中途离场线程取消的优雅解决方案在并发世界中线程等待可能被中断或超时这就需要它能中途离开队列。假设我们使用单向链表如CLH锁的原版实现要删除中间节点会遇到大问题要删除节点B必须知道它的前驱节点A但在单向链表中节点B并不知道谁指向自己。双向链表的优势每个节点都有前驱指针删除任意节点只需修改相邻节点的指针// 简化版的节点删除逻辑node.prev.nextnode.next;if(node.next!null){node.next.prevnode.prev;}这种操作的时间复杂度是O(1)而单向链表需要从头遍历时间复杂度为O(n)。现实比喻想象超市结账队伍中有人突然接到电话要离开。如果是单向排队每个人只记得前面是谁离开的人需要大喊我是谁前面的人让队伍重新整理。而双向排队每个人记得前后是谁离开只需与前后的人沟通即可不影响其他人。2. 从轮询到唤醒性能的巨大飞跃原始CLH锁通过线程不断轮询前驱状态来判断是否轮到自己。这种忙等待在竞争激烈时会导致大量CPU资源浪费。AQS的创新在于线程经过短暂尝试后如果未能获取锁就会主动进入休眠等待被唤醒。这就需要一个机制来确保释放锁的线程能准确找到下一个应该运行的线程。双向链表中的next指针正是实现精准唤醒的关键// 释放锁时唤醒后继节点的核心逻辑Nodesnode.next;if(s!nulls.thread!null){LockSupport.unpark(s.thread);}通过next指针释放锁的线程可以直接找到后继节点并唤醒它实现了从被动轮询到主动唤醒的进化。3. 状态的高效传播共享模式的精髓在共享模式如Semaphore、CountDownLatch下资源释放可能需要连续唤醒多个等待线程。当第一个共享节点被唤醒并获取资源后如果还有剩余资源它会通过next指针找到并唤醒后继节点形成唤醒传播链。// 共享模式下唤醒传播的简化逻辑if(propagate0||hnull||h.waitStatus0){Nodesnode.next;if(snull||s.isShared()){doReleaseShared();// 继续唤醒后继节点}}这种连锁反应依赖于next指针的高效遍历是共享同步器高性能的关键。三、实战分析ReentrantLock中的双向链表应用让我们看一个ReentrantLock的典型场景了解双向链表如何工作ReentrantLocklocknewReentrantLock();// 线程Alock.lock();try{// 临界区操作}finally{lock.unlock();}// 同时线程B、C、D也尝试获取锁线程A首先获取锁state变为1线程B尝试获取失败被加入队列head → Btail线程C也获取失败加入队尾head → B → Ctail线程D同样加入head → B → C → Dtail此时如果线程C因中断取消等待双向链表的优势就体现出来了通过C的prev指针找到B将B的next指向D通过C的next指针找到D将D的prev指向BC被成功移除head → B → Dtail这个过程中不需要遍历整个队列即使队列有1000个节点删除操作也是常量时间。四、设计哲学的思考空间换时间的经典权衡AQS选择双向链表而非单向链表体现了经典的空间换时间权衡空间开销每个节点增加一个prev指针8字节在大多数场景下可忽略不计时间收益节点删除操作从O(n)提升到O(1)唤醒操作更加精准高效在并发编程中减少竞争通常比优化单线程速度更重要。双向链表通过减少全局竞争显著提升了高并发下的吞吐量。就像城市交通规划增加一些立体交叉桥空间投入可以避免主要路口的拥堵时间收益整体通行效率反而提升。五、总结AQS选择双向链表不是随意之举而是基于以下深思熟虑高效取消线程中断或超时时能快速移除节点避免遍历开销精准唤醒通过next指针直接定位后继节点实现主动唤醒而非忙等待状态传播共享模式下支持连续唤醒提高资源利用率设计平衡以微小空间代价换取显著性能提升双向链表在AQS中的应用体现了并发大师Doug Lea对实际应用需求的深刻理解。它不是理论上的最优解而是工程实践中的最佳平衡。下次使用ReentrantLock或CountDownLatch时不妨想想背后那个默默工作的双向链表正是这个看似简单的数据结构支撑起了Java并发编程的半壁江山。参考文档https://blog.csdn.net/u012503481/article/details/108185186https://blog.csdn.net/hlqionga/article/details/148868791https://segmentfault.com/a/1190000046120153https://blog.csdn.net/weixin_58316280/article/details/145604477https://blog.csdn.net/Cobbyer/article/details/106316582http://www.cnblogs.com/hanease/p/14897445.htmlhttps://blog.csdn.net/weixin_48302711/article/details/126793986本文由Java并发技术爱好者撰写欢迎关注公众号后端技术精粹获取更多深度技术解析。更多技术干货欢迎关注微信公众号科威舟的AI笔记~【转载须知】转载请注明原文出处及作者信息

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询