美文网首页
深入解析AbstractQueuedSynchronizer源码

深入解析AbstractQueuedSynchronizer源码

作者: hcy0411 | 来源:发表于2018-09-05 14:41 被阅读0次

    前面分析了AbstractQueuedSynchronizer实现的其他两部分:
    Condition源码解析
    独占模式解析
    本文继续介绍AbstractQueuedSynchronizer最后一部分功能--共享模式

    共享模式资源获取

    共享模式获取资源的入口如下

    // 忽略中断异常
       public final void acquireShared(int arg) {
           if (tryAcquireShared(arg) < 0)
               doAcquireShared(arg);
       }
      // 抛出中断异常
       public final void acquireSharedInterruptibly(int arg)
               throws InterruptedException {
           if (Thread.interrupted())
               throw new InterruptedException();
           if (tryAcquireShared(arg) < 0)
               doAcquireSharedInterruptibly(arg);
       }
       // 超时获取,并抛出中断异常
       public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
               throws InterruptedException {
           if (Thread.interrupted())
               throw new InterruptedException();
           return tryAcquireShared(arg) >= 0 ||
               doAcquireSharedNanos(arg, nanosTimeout);
       }
    

    tryAcquireShared留给工具自己去实现,用于判断是否满足获取资源要求,与独占模式判断函数不一样,tryAcquire返回的是boolean,因为是独占模式,每次只能一个线程获取资源,所以直接返回boolean,共享模式下资源可以被多个线程通知占用,tryAcquireShared返回int类型,表示还有多少个资源可以同时被占用,用于共享模式下传播唤醒。

    doAcquireShared

    重点分析下doAcquireShared方法,doAcquireSharedInterruptibly和doAcquireSharedNanos区别不是很大,不做分析

        private void doAcquireShared(int arg) {
            //添加共享模式节点,主要区分独占模式
            final Node node = addWaiter(Node.SHARED);
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    //拿到当前节点的前驱节点,如果前驱节点是head节点,说明没有等待节点
                    final Node p = node.predecessor();
                    if (p == head) {
                        // 尝试获取资源,大于等于0,说明有资源获取。
                        int r = tryAcquireShared(arg);
                        if (r >= 0) {
                            //把当前节点设置成head节点,并传播唤醒后面的节点。
                            setHeadAndPropagate(node, r);
                            p.next = null; // help GC
                            if (interrupted)
                                selfInterrupt();
                            failed = false;
                            return;
                        }
                    }
                    // 这里和独占模式一样,如果没资源申请,封装节点,并park等待
                    if (shouldParkAfterFailedAcquire(p, node) &&
                            parkAndCheckInterrupt())
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
    

    setHeadAndPropagate

        private void setHeadAndPropagate(Node node, int propagate) {
            // 设置head节点,保留之前的head的节点,用于是否传播唤醒之后节点判断
            Node h = head; // Record old head for check below
            setHead(node);
    
            /**
             * 需要传播唤醒的几个条件
             * 1,propagate>0,当大于0的时候,说明还有其他资源空余,需要传播唤醒之后的节点
             * 2,head == null || head.waitStatus < 0,头结点为空,head.waitStatus < 0即需要唤醒状态或者是传播状态,也无条件尝试唤醒之后的节点
             * 唤醒之后的节点,会去重新尝试获取资源。
             * 这里存在误唤醒,不过没关系,唤醒之后的节点,会继续回到doAcquireShared for循环中,尝试获取资源。
             */
            if (propagate > 0 || h == null || h.waitStatus < 0 ||
                    (h = head) == null || h.waitStatus < 0) {
                Node s = node.next;
                // 判断是否是共享节点
                if (s == null || s.isShared())
                    doReleaseShared();
            }
        }
    

    以上是共享模式获取资源流程,释放资源函数如下:

    doReleaseShared

        private void doReleaseShared() {
            for (;;) {
                Node h = head;
                if (h != null && h != tail) {
                    int ws = h.waitStatus;
                    // 如果head节点的后继节点需要被唤醒,然后唤醒
                    if (ws == Node.SIGNAL) {
                        if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                            continue;            // loop to recheck cases
                        unparkSuccessor(h);
                    }
                    // 如果head节点后没有需要被唤醒的节点,设置成PROPAGATE状态,然后传播唤醒
                    else if (ws == 0 &&
                            !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                        continue;                // loop on failed CAS
                }
                if (h == head)                   // loop if head changed
                    break;
            }
        }
    

    相关文章

      网友评论

          本文标题:深入解析AbstractQueuedSynchronizer源码

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