美文网首页
AQS源码解析(1)acquireQueued

AQS源码解析(1)acquireQueued

作者: 三斤牛肉 | 来源:发表于2021-01-06 11:43 被阅读0次

    加锁的底层调用acquire函数:
    tryAcquire由具体实现类实现,本节看acquireQueued函数

    public final void acquire(int arg) {
            //
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }
    

    该函数表示将已经在队列中的node(每个线程对应一个node加到等待队列中,具体以后分析)尝试去获取锁否则挂起

    final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {//死循环
                    final Node p = node.predecessor();//获得该node的前置节点
                    /**
                    * 如果前置节点是head,表示之前的节点就是正在运行的线程,表示是第一个排队的
    (一般讲队列中第一个是正在处理的,可以想象买票的过程,第一个人是正在买票(处理中),第二个才是真正排队的人);
    那么再去tryAcquire尝试获取锁,如果获取成功,说明此时前置线程已经运行结束,则将head设置为当前节点返回
                    *
                    *
                    **/
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);
                        p.next = null; // help GC,将前置节点移出队列,这样就没有指针指向它,可以被gc回收
                        failed = false;
                        return interrupted;//返回false表示不能被打断,意思是没有被挂起,也就是获得到了锁
                    }
                    /**shouldParkAfterFailedAcquire将前置node设置为需要被挂起,
                        注意这里的waitStatus是针对当前节点来说的,
                        即是前置node的ws指的是下一个节点的状态**/
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())//挂起线程 park()
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);//如果失败取消尝试获取锁(从上面的代码看只有进入p == head && tryAcquire(arg)这个逻辑是才会触发,这个时候前置节点正好在当前节点入队的时候执行完,当前节点正好获得锁,具体的代码以后分析)
            }
        }
    //看到因为是死循环,所以当执行到parkAndCheckInterrupt()时,当前线程被挂起,等到某一天被unpark继续执行,这个时候已经是对头的第二个节点了,那么就会进入if (p == head && tryAcquire(arg))逻辑获取到锁并结束循环
    
    private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);
            return Thread.interrupted();//这里返回是否被打断,在lockInterruptibly中有意义,在lock中没有意义,个人理解只是为了代码复用。
        }
    

    总结

    我们来描绘一下排队买票的场景:
    当你去买票的时候,首先要看一下窗口有没有人正在受理(tryAcquire),如果没有那么你可以去受理,否则就排在队尾(addWaiter),当然你走到队尾的时候也许受理的人已经好了,所以你要再瞄一眼(acquireQueued中p == head && tryAcquire逻辑)看看能不能去受理,发现还不行那就开始耍手机(acquireQueued中shouldParkAfterFailedAcquire(p, node) &&
    parkAndCheckInterrupt逻辑)(当然如果你不是排队的第一个人,就没有必要去瞄一眼直接刷手机就好了)。

    相关文章

      网友评论

          本文标题:AQS源码解析(1)acquireQueued

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