首先我们看一下AQS的入队操作:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 如果队列为null(连头节点都没有)
if (t == null) { // Must initialize
// 初始化头结点
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 将该节点插入头结点后
node.prev = t;
if (compareAndSetTail(t, node)) {
// =======================
t.next = node;
return t;
}
}
}
}
以上就是AQS,线程的入队方法,注意看上述代码中我用“======”标出来的分割线,当执行完compareAndSetTail(t, node)后,t.next = node 还没执行,那么其他线程A这个时刻看队列应该是这个样子:
即,tail的prev连上了,next还没连上。因此AQS,入队操作是非原子性的。
理解了上面那个,我们再来介绍一下hasQueuedPredecessors方法,该方法的作用是,判断在当前线程之前是否有其他线程在排队,在ReentrantLock公平锁模式下的tryAcquire()内会调用这个方法。 来看一下hasQueuedPredecessors的源码:
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
源码看起来很简单很简单,仅是根据一句话就能实现所需的功能。 下面我们分两种情况来讨论一下,执行这个方法会发生什么:
①:线程没有入队
AQS的acquire()函数,在没入队之前会执行一下tryAcquire()方法,而在tryAcquire()内会调用hasQueuedPredecessors()方法,这时我们看一下,h!=t ,如果为true的话,至少可以说明队列中起码存在一个线程(节点),单凭这个就足以返回true了,因为当前线程还没入队呢,队列中已经有一个节点了,那这个节点肯定在他前面呀。 由于是h != t &&((s = h.next) == null || s.thread != Thread.currentThread());是通过 “&&”来连接两个条件,因此还需要(s = h.next) == null || s.thread != Thread.currentThread()为true,最终结果才能返回true,如果(s = h.next) == null为true,说明头结点的next为空,但是刚刚已经说了,队列中至少有一个节点了,头结点的next节点怎么可能是空呢,只能说明其他线程在执行入队操作,已经入队了,但是next还没连上,所以当前线程看头结点的next是空的。所以(s = h.next) == null为true,也能说明队列中至少存在一个节点。 如果(s = h.next) == null为false(说明头结点的next连上了后驱节点),那么s.thread != Thread.currentThread()一定为true,因为当前线程还没入队,s.thread是队列中的线程,这肯定不能呀。 综上:如果线程还没入队,且h != t 为true,那么(s = h.next) == null || s.thread != Thread.currentThread()一定也为true,返回结果就是true!
②:线程已入队(已经执行过入队方法了)
当线程执行tryAcquire()获取同步资源失败的话,当前线程会入队。 既然入队了,是不是说明当前线程所在节点的前驱结点的next已经连上了当前线程所在节点了,先想下这句话。 接下来我们依旧假设h != t 为true,说明线程中至少已经存在一个线程了。 接下来看一下,为什么(s = h.next) == null为true,就能说明有前驱结点了呢? 我们假设(s = h.next) == null为true,我们想一下此时队列的状态:
1.至少存在一个节点
2.头结点的next为空
3.当前线程所在节点的前驱结点的next已经连上了当前线程所在节点了(关键点)
要满足上面三种情况,那么队列的状态只能至少是:
我们看上图,因此可以推测出,(s = h.next) == null 为 true,则当前线程之前一定有一个已经在排队的线程
再来看如果(s = h.next) == null为false,如果s.thread != Thread.currentThread()为true,则当前线程之前一定有一个已排队的节点,因为s代表头结点的后一个head后驱节点,如果head后驱节点对应的线程和当前线程不相等,那肯定至少说明s节点排在当前线程前面呀,所以假设成立。