Java并发(五)

本文详细介绍了Java并发中的AbstractQueuedSynchronized(AQS)的hasQueuedPredecessors、setExclusiveOwnerThread、tryAcquire方法,以及公平锁的排队机制。AQS的acquire方法中涉及的acquireQueued和相关自旋、挂起策略。讨论了线程在获取锁过程中的状态变化和线程间协作的实现细节。
摘要由CSDN通过智能技术生成

就是返回了底层的state变量,也就是AQS的state

[](()hasQueuedPredecessors方法

这个方法是用来判断线程是否需要排队的(返回false不需要排队),因为是公平锁,所以这个判断是必须的,否则就不公平了,因为没有判断排队操作,那么只要一个线程释放了锁,那么后来的线程也是可以抢到的(透露一下:非公平锁的实现就是仅仅少了这个判断而已)

在这里插入图片描述

可以看到,这个方法是AQS里面的,tail和head属性前面已经提到过,是底层队列的头尾结点

关键在于return语句

他主要有两个判断

  • 头结点是否等于尾结点

  • 头结点是否尾空,或者头结点的线程是否为当前线程

关于这个弹出,必须要先清楚,底层队列里面的结点是怎么添加的

下面就是AQS的插入方法addWaiter

在这里插入图片描述

可以看到,如果尾结点不为空,也就代表不是第一次插入,那么直接就使用尾插法,如果是第一次插入,只是调用enq方法,所以enq方法是针对第一次插入使用的,这两个方法都是AQS的

在这里插入图片描述

可与看到里面是一个死循环,如果是第一次插入,让创建一个新结点为头结点(相当于一个哨兵),然后让新尾结点一样,这一点可以看成是初始化头尾结点,接下来下一轮循环,此时尾结点已经不为null,接下来就让新结点指向尾结点,让新结点又称为了尾结点,再让旧的尾结点指向新的尾结点

但这里有一个疑问,如果并不是第一次入队,为什么最后还要执行多一次enq方法呢?

我们可以看到,如果并不是第一次入队,因为前面的addWaiter,使用了一次CAS,那如果CAS失败了怎么办?即中途有线程把尾结点改了怎么办?

此时就需要重复的操作,不断的执行CAS直到成功为止,所以就有了无论哪次插入都要执行enq方法,因为enq方法里面就是一个死循环的CAS执行,直到CAS执行成功,才会结束

总结一下添加结点

  • 所以头结点只是一个哨兵,是不存储线程的,而插入的方法则是尾插法

  • 其实头结点也不完全是哨兵,下面我们看线程自旋时,会发现其实头结点是当前轮到执行的线程,这里先埋下个伏笔

知道了插入方法后,我们回过头来看hasQueuedPredecessors的返回值,也就是如何判断是否需要排队

在这里插入图片描述

步骤如下

  • 判断头结点是否等于尾结点,只有在初始化的时候,头结点才会等于尾结点,或者队列为空,头尾结点都为null,所以,如果头结点等于尾结点(直接返回false),就代表了队列为空,所以就不需要进行排队

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值