从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队列的创建和变化过程。

posted @ 2020-11-18 00:13  皮卡丘和羊宝贝😄  阅读(175)  评论(0编辑  收藏  举报