深入解析AbstractQueuedSynchronizer源码

2018-09-05  本文已影响0人  hcy0411

前面分析了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;
        }
    }
上一篇下一篇

猜你喜欢

热点阅读