从ReentrantLock实现非公平锁的源码理解AQS中的CLH队列
虽然前面也看过AQS的文章,并且转载过一篇大佬的分析,但是我觉得他们对于AQS和ReentrantLock部分的源码的分析并不详细,自己理解期来还是有问题,于是自己准备花时间重新梳理下,好了,进入正题。
第一个线程过来加锁
我们看的是非公平锁的,这里进入nonfair实现:
代码如下:
由于这是第一个线程过来获取锁,所以这里通过cas方式加锁成功,即通过compareAndSetState方法,成功的将state共享变量的值设置为1,并将the owner thread设置为当前线程。这里假设大家已经看过前面连篇关于cas的文章了AQS简单理解入门,彻底掌握 AQS
这里线程1已经加锁成功了,我们假设它暂时是不会释放锁的,因为主要是看aqs内部的队列。
第二个线程尝试过来加锁
和第一个线程一样,仍然走进lock方法的非公平实现:
但是此时由于线程1已经加锁成功,所以cas是失败的,进入到else块的acqure方法里
这里可以看到,进入acquire方法里,是先调用tryAcquire方法,尝试下,看看能不能加上锁,万一线程1释放了锁呐,这样就不用入队列,我们直接进入到非公平锁的nonfairTryAcquire方法:
由于我们假设线程1需要占用很久,所以这时tryAcquire()获取锁是失败的,返回为false:
这里我们先进去这个addWaiter方法:
在分析这个方法之前,需要先补充下双向链表的知识,要不这里会迷,我们先看官方源码中的注释:
由于线程1成功获取锁,并没有被阻塞放入CLH队列,且这里由于clh队列是懒加载的,并没有初始化,这时,head和tail指针指向的头尾node节点均为null:
进入addWaiter:
进入enq方法,参数为node = new Node(Thread.currentThread(), null);
这里来张图展示下clh队列的变化:
方法enq执行完出来后,addWaiter方法也执行结束,并返回创建的node:
此时回到调用addWaiter方法处,进入acquireQueued方法:
但是排在队列第二位的线程,如果尝试几次不成功后,也会像后面的线程一样,最后走进shouldParkAfterFailedAcquire()方法:
这里暂时不分析了,通过上面的分析,已经达到本文的目的,理解clh队列的创建和变化过程。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构