双向链表的每个数据节点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点(head)和后继结点(tail)。
AQS实际上通过头尾指针来管理同步队列,同时实现包括获取锁失败的线程进行入队,释放锁时对同步队列中的线程进行通知等核心方法。
一、添加节点
-
如果队列为空,会初始化队列。
第一个加进来的新节点既是头也是尾,前后都没有节点。
- 新增一个节点
添加节点会涉及到三个变化:
- 新的线程封装成Node节点追加到同步队列中,设置prev节点以及修改当前节点的前置节点的next节点指向自己
-
通过CAS讲tail重新指向新的尾部节点
-头节点会更新waitStatus为-1
二、移除节点
我们以下图来介绍下等待队列时如何移除节点的。如图中所示队列中有t1,t2两个线程。假设此时持有锁的线程t0释放锁unlock。那么队列中线程移除步骤如下:
- 线程先释放state资源
- 通过 unparkSuccessor 唤醒head的后继节点,因为后继节点有可能进入了waitting状态,将waitStatus值设为0.
- 判断node的后继节点next是否有效,如果无效,则从队列的tail节点开始往前遍历,直到找到第一个有效的节点
- 唤醒线程后,在acquireQueued自旋获取锁,头节点移除队列,获取锁的节点设置为头。