美文网首页
探索AQS等待队列-哨兵节点

探索AQS等待队列-哨兵节点

作者: 隐藏的记号 | 来源:发表于2019-12-07 23:59 被阅读0次

Question:AQS的等待队列初始化后,Head节点会一直是哨兵节点吗?

why is the question:

在AQS中,线程竞争锁失败后将被放入AQS实例的等待队列中。

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
image.png image.png

第一次初始化等待队列,队列中队头为哨兵节点。而后,每次真正的线程节点都是拼接在尾部。

比如,在食堂等待打饭菜的队伍中,同学A开始找到了队尾开始入队,排在A前面的是同学B。当前关系是,A的前面是B,队尾不再是B,而是新入队的A。如果要满足双向链表定义,则B的后面是A。

当多个线程竞争同一个锁失败后,都进入了等待队列,队列的情况大概是这样的:

哨兵节点 ← node-1 ←  node-2 ← node-3 

tail节点是Node · 3,如果这时候当前持有锁线程释放锁,假设按照公平锁策略,node-1将要获取锁,可是我看到AQS中释放锁代码部分却是:

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);  // 将头部节点传入了unpark方法
            return true;
        }
        return false;
    }

释放锁的操作,选定了 head 节点作为下一个线程激活对象,可这head可不是哨兵节点嘛?哨兵节点里面并没有其代表的线程,源码中这么写是为什么?

try geting answer:

在前面写问题过程时,就突然想到了,head节点虽然哨兵节点,但是 head.next不就是等待队列中最早等待的那个线程吗?

点开unparkSuccessor(h),这个方法里面激活的还真的是head.next,说明前面的问题其实是肯定的,head节点,在队列初始化之后,一直都是哨兵节点。

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        // 实际激活的线程节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

相关文章

  • 探索AQS等待队列-哨兵节点

    Question:AQS的等待队列初始化后,Head节点会一直是哨兵节点吗? why is the questio...

  • AQS

    AQS(AbstractQueuedSynchronizer) node node是等待队列(双向队列)的节点分别...

  • AQS源码浅析

    AQS基本结构 阻塞队列是从head后的结点开始的,也即 不包含头节点head 哨兵结点head作用 因为AQS本...

  • J.U.C之AQS:源码解析-核心属性

    AQS 核心属性 AQS 内部提供了一个内部类.用来作为同步队列和等待队列的节点对象.不同队列的节点.其使用的属性...

  • Day30

    AQS 同步等待队列 & 条件等待队列 ReentrantLock同步执行,类似synchronized可重入 s...

  • AQS源码浅析(2)——属性及结构

    数据结构 AQS内部是一个双向链表,其中head是一个虚节点。 Node注释翻译 等待队列是“CLH”(Craig...

  • AbstractQueuedSynchronizer (AQS)

    双队列 在AQS中,存在两个队列 等待队列:用于挂起当前线程,等待某个条件满足后唤醒或是被中断。 同步队列:多线程...

  • 六、AQS源码分析

    〇、前置问题 同步队列,等待队列和条件队列的区别? AQS和monitor区别?队列的对比? 类继承结构? 一、A...

  • Java基础建设 4-AQS源码分析

    一、AQS分析 1.原理概述 2.重要属性 Node节点 Head/Tail条件队列的头尾节点,Head节点是已经...

  • Day 29 AQS

    AQS 具备特性 阻塞,等待独占,共享公平,非公平可重入允许中断. . 同步等待队列clh,双向链表构筑的队列 条...

网友评论

      本文标题:探索AQS等待队列-哨兵节点

      本文链接:https://www.haomeiwen.com/subject/vfpkyctx.html