目录
- 1. 说明
- 2. 双向链表的特性
- 2.1 双向指针
- 2.2 高效操作
- 2.3 支持双向遍历
- 3. AQS的设计需求
- 3.1 管理等待线程
- 3.2 高效传播状态信息
- 3.3 支持异常处理
- 3.4 简化自旋竞争锁的逻辑
- 4. 单向链表的局限性
- 4.1 遍历方向受限
- 4.2 节点删除操作复杂
- 4.3 状态信息传播不便
1. 说明
- 1.AbstractQueuedSynchronizer(AQS)采用双向链表的原因,主要基于双向链表自身的特性和AQS的设计需求。
2. 双向链表的特性
2.1 双向指针
- 1.双向链表中的每个节点都有两个指针,一个指向前驱节点,一个指向后继节点。
- 2.从任何一个节点出发,都可以方便地访问其前驱和后继节点。
2.2 高效操作
- 1.双向链表支持在任意节点位置进行数据的插入和删除。
- 2.这些操作的时间复杂度都是O(1),即不受链表长度的影响。
- 3.这对于需要频繁对链表进行增删操作的场景非常有用。
2.3 支持双向遍历
- 1.双向链表可以在任何一个节点方便地进行向前或向后的遍历,这对于有反向遍历需求的场景来说非常有用。
3. AQS的设计需求
3.1 管理等待线程
- 1.AQS使用双向链表来实现等待队列,用于管理在同步器上等待的线程。
- 2.当线程尝试获取锁但发现锁被占用时,该线程会加入到等待队列中等待。
- 3.双向链表可以保持线程加入等待队列的顺序,即先加入的线程排在队列前面,后加入的线程排在队列后面,这有助于实现公平性。
3.2 高效传播状态信息
- 1.在等待队列中,线程的状态可能会发生变化。
- 2.当前驱节点释放锁时,需要唤醒后继节点。
- 3.双向链表的结构可以使得这种状态信息的传播更加高效。
- 4.每个节点都有指向其前驱和后继节点的引用,因此当前驱节点的状态发生变化时,可以立即影响到后继节点。
3.3 支持异常处理
- 1.在同步队列中,可能存在因为异常或其他原因而不再需要竞争锁的线程。
- 2.这些线程对应的节点需要从链表中删除。
- 3.双向链表可以使得这种删除操作更加高效,因为可以直接通过前驱节点的引用找到需要删除的节点,而无需从头节点开始遍历。
3.4 简化自旋竞争锁的逻辑
- 1.为了提高性能,新加入到链表中的线程会首先通过自旋的方式尝试竞争锁。
- 2.在自旋竞争锁的过程中,需要判断当前线程所在节点的前驱节点是否是头节点。
- 3.如果是头节点的下一个节点,则有必要去竞争锁;否则,就没必要再去触发锁竞争的动作。
- 4.双向链表可以使得这种判断更加高效,因为可以直接通过前驱节点的引用进行判断。
4. 单向链表的局限性
4.1 遍历方向受限
- 1.单向链表只能从头节点开始顺序遍历到尾节点,无法从尾节点向前遍历。
- 2.这种限制在AQS中可能导致某些操作变得复杂或低效,特别是当需要反向查找或处理节点时。
4.2 节点删除操作复杂
- 1.在单向链表中,删除一个节点需要知道其前驱节点(除非该节点是头节点)。
- 2.而在AQS中,当某个线程释放锁并需要唤醒后继线程时,如果采用单向链表,则需要从头节点开始遍历以找到要删除和唤醒的节点,这会增加时间复杂度。
4.3 状态信息传播不便
- 1.在单向链表中,状态信息(如锁状态、线程状态等)的传播通常依赖于遍历链表。
- 2.而在AQS中,状态信息的快速传播对于提高同步器的性能至关重要。
- 3.双向链表可以使得状态信息在节点之间更加高效地传播。