公平锁下的hasQueuedPredecessors原理解析

首先我们看一下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节点排在当前线程前面呀,所以假设成立。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值