一、AQS高频问题:
1.1 AQS是什么?
AQS就是一个抽象队列同步器,abstract queued sychronizer,本质就是一个抽象类。
AQS中有一个核心属性state,其次还有一个双向链表以及一个单项链表。
首先state是基于volatile修饰,再基于CAS修改,同时可以保证三大特性。(原子,可见,有序)
其次还提供了一个双向链表。有Node对象组成的双向链表。
最后在Condition内部类中,还提供了一个由Node对象组成的单向链表。
AQS是JUC下大量工具的基础类,很多工具都基于AQS实现的,比如lock锁,CountDownLatch,Semaphore,线程池等等都用到了AQS。
state是啥:state就是一个int类型的数值,同步状态,至于到底是什么状态,看子类实现。
condition和单向链表是啥:都知道sync内部提供了wait方法和notify方法的使用,lock锁也需要实现这种机制,lock锁就基于AQS内部的Condition实现了await和signal方法。(对标sync的wait和notify)
sync在线程持有锁时,执行wait方法,会将线程扔到WaitSet等待池中排队,等待唤醒
lcok在线程持有锁时,执行await方法,会将线程封装为Node对象,扔到Condition单向链表中,等待唤醒
Condition在做了什么:将持有锁的线程封装为Node扔到Condition单向链表,同时挂起线程。如果线程唤醒了,就将Condition中的Node扔到AQS的双向链表等待获取锁。
1.2 唤醒线程时,AQS为什么从后往前遍历?
如果线程没有获取到资源,就需要将线程封装为Node对象,安排到AQS的双向链表中排队,并且可能会挂起线程
如果在唤醒线程时,head节点的next是第一个要被唤醒的,如果head的next节点取消了,AQS的逻辑是从tail节点往前遍历,找到离head最近的有效节点?
想解释清楚这个问题,需要先了解,一个Node对象,是如何添加到双向链表中的。
基于addWaiter方法中,是先将当前Node的prev指向tail的节点,再将tail指向我自己,再让prev节点指向我
如下图,如果只执行到了2步骤,此时,Node加入到了AQS队列中,但是从prev节点往后,会找不到当前节点。
1.3 AQS为什么用双向链表,(为啥不用单向链表)?
因为AQS中,存在取消节点的操作,节点被取消后,需要从AQS的双向链表中断开连接。
还需要保证双向链表的完整性,
- 需要将prev节点的next指针,指向next节点。
- 需要将next节点的prev指针,指向prev节点。