Untitled

一个线程获取锁失败后,会被包装为 Node 节点接入到 CLH 同步队列中,CLH 同步队列是一个 FIFO 的队列,理论上来说实现一个单向的就可以了,为什么要设计为双向的呢?其实 JDK 注释就已经说明了答案:

中断操作需要在 CLH 同步队列中删除节点 Node,这个时候就转变为了删除节点的问题了。如果我们要从 CLH 单向队列中删除中间节点,则会有点儿困难。因为,直接维护 prev、next指针,降低了删除操作的复杂度

当线程获取锁失败后,它会加入 CLH 队列,同时通过自旋的方式获取锁,但实际上按照公平锁的原则,只有 head 的下一个节点才有必要去竞争锁,其他的节点完全没有必要。所以为了避免这个问题,加入到链表的节点在尝试竞争锁之前,会判断它的前置节点是否为 head,如果不是,则没有必要去触发锁竞争的动作,何必多此一举呢?

AQS 还支持条件变量,它允许线程在特定条件下挂起和继续执行。这就需要将线程从同步队列转移到条件队列,然后再转回。在这种情况下,如果是单向队列则会使整个过程变得比较复杂,而双向队列则使得节点的移动更加高效和灵活。