AQS是什么?
AQS就是一个抽象队列同步器,abstract queued sychronizer,本质就是一个抽象类。
AQS中有一个核心属性state,其次还有一个双向链表以及一个单项链表。
首先state是基于volatile修饰,再基于CAS修改,同时可以保证三大特性。(原子,可见,有序)
其次还提供了一个双向链表。有Node对象组成的双向链表。
最后在Condition内部类中,还提供了一个由Node对象组成的单向链表。
AQS是JUC下大量工具的基础类,很多工具都基于AQS实现的,比如lock锁,CountDownLatch,Semaphore,线程池等等都用到了AQS。
AQS为什么从后往前遍历?
如果线程没有获取到资源,就需要将线程封装为Node对象,安排到AQS的双向链表中排队,并且可能会挂起线程
如果在唤醒线程时,head节点的next是第一个要被唤醒的,如果head的next节点取消了,AQS的逻辑是从tail节点往前遍历,找到离head最近的有效节点?
想解释清楚这个问题,需要先了解,一个Node对象,是如何添加到双向链表中的。
基于addWaiter方法中,是先将当前Node的prev指向tail的节点,再将tail指向我自己,再让prev节点指向我
如下图,如果只执行到了2步骤,此时,Node加入到了AQS队列中,但是从prev节点往后,会找不到当前节点。
AQS为什么使用双向链表?
因为AQS中,存在取消节点的操作,节点被取消后,需要从AQS的双向链表中断开连接。
还需要保证双向链表的完整性,
-
需要将prev节点的next指针,指向next节点。
-
需要将next节点的prev指针,指向prev节点。
如果正常的双向链表,直接操作就可以了。
但是如果是单向链表,需要遍历整个单向链表才能完成的上述的操作。比较浪费资源。
AQS为什么有一个虚拟的head节点?
有一个哨兵节,点更方便操作。
另一个是因为AQS内部,每个Node都会有一些状态,这个状态不单单针对自己,还针对后续节点
-
1:当前节点取消了。
-
0:默认状态,啥事没有。
-
-1:当前节点的后继节点,挂起了。
-
-2:代表当前节点在Condition队列中(await将线程挂起了)
-
-3:代表当前是共享锁,唤醒时,后续节点依然需要被唤醒。
Node节点的ws,表示很多信息,除了当前节点的状态,还会维护后继节点状态。
如果取消虚拟的head节点,一个节点无法同时保存当前阶段状态和后继节点状态。
同时,在释放锁资源时,就要基于head节点的状态是否是-1。来决定是否唤醒后继节点。
如果为-1,正常唤醒
如果不为-1,不需要唤醒吗,减少了一次可能发生的遍历操作,提升性能。