1 引言 

上篇文章讲述了java AQS构造以及其中排他API的 实现逻辑。而这一篇咱们来看看其共享逻辑, 这里仍旧应用上文的火车买票为例,便于了解。这里间接会从代码实现讲起,对于还不理解AQS的构造的倡议先看一下上一篇文章:用火车购票的形式关上 AQS同步器(一)

2 AQS共享和排他逻辑的区别

共享实现形式与排他逻辑十分相似(毕竟是框架嘛)。排他逻辑其实能够看作是共享逻辑的一种特例,因为它的独占个性。排他逻辑tryAcquire办法返回值为虚实,示意是否资源(一票一车的状况)曾经被其他人占用。而共享逻辑tryAcquireShared办法返回值为资源数,只有当资源数<0,才示意以后无奈获取执行权,它更像现实生活中的买票行为。而在期待状态时两者也体现出一些不同,代码里会讲到。

3 AQS同步器共享逻辑代码实现

共享(资源共享,只有买了车票后,就都能够上这辆车):

## 获取车票,没有则进入期待public final void acquireShared(int arg) {            if (tryAcquireShared(arg) < 0) // 尝试购票,只有票数 > 0,就能够胜利上车                    doAcquireShared(arg); // 车票售完了,就须要期待了    }
 ## 如果本人后面是第一人,就尝试候补,否则就期待。  private void doAcquireShared(int arg) {         final Node node = addWaiter(Node.SHARED);  // 以共享模式退出到队列中;        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {                final Node p = node.predecessor();                if (p == head) { // 是队列第一个,                    int r = tryAcquireShared(arg); // 那么本人也能够去尝试候补                    if (r >= 0) { // 有>1张车票                        setHeadAndPropagate(node, r);  // 设置本人为第一个人,并有多余车票信息向后流传                        p.next = null; // help GC                        if (interrupted)                            selfInterrupt();                        failed = false;                        return;                    }                }               if (shouldParkAfterFailedAcquire(p, node) && //  与排他逻辑统一                  parkAndCheckInterrupt())                     interrupted = true;              }        } finally {            if (failed)                cancelAcquire(node);        }    }
 ## 将本人排第一位,并在车票多,并在状态<0就尝试告诉前面的人 private void setHeadAndPropagate(Node node, int propagate) { // node : 新来候补的人,pro..:车票数        Node h = head; // Record old head for check below        setHead(node);  // 以后人开始候补,排队为第一人        /*         * Try to signal next queued node if:         *   Propagation was indicated by caller,         *     or was recorded (as h.waitStatus either before         *     or after setHead) by a previous operation         *     (note: this uses sign-check of waitStatus because         *      PROPAGATE status may transition to SIGNAL.)         * and         *   The next node is waiting in shared mode,         *     or we don't know, because it appears null         *         * The conservatism in both of these checks may cause         * unnecessary wake-ups, but only when there are multiple         * racing acquires/releases, so most need signals now or soon         * anyway.         */        if (propagate > 0 || h == null || h.waitStatus < 0 ||              (h = head) == null || h.waitStatus < 0) {            Node s = node.next;            if (s == null || s.isShared())                doReleaseShared(); // 持续唤醒        }    }## 这里要阐明一下 if中的条件:就如同正文所说,这里可能会引起不必要的唤醒。propagate 指的是资源数,很好了解>0张前面的人天然能够筹备候补。前面两个h.waitStatus < 0:前者为旧head的状态。小于0(PROPAGATE,在releaseShare办法(并发调用)中有可能设置为该值),阐明有多余的车票呈现了后者为新head < 0,那么其实无论是PROPAGATE 还是SINGAL状态,其后一位都能够尝试后不了这里能够这么了解:既然有可能马上轮到我,或者车票可能有多余的话,我就能够告诉这些人进行尝试候补了,这些人获取车票的机会很大

开释资源(其中有人退票或者下车了,就又有空余的车票了):

 public final boolean releaseShared(int arg) {        if (tryReleaseShared(arg)) { // 下车或者退票(用于子类重写)            doReleaseShared(); // 告诉还在排队的人筹备候补            return true;        }        return false;    }
## 须要告诉前面的人候补,或者标记可能多张车票开释了private void doReleaseShared() {        /*         * Ensure that a release propagates, even if there are other         * in-progress acquires/releases.  This proceeds in the usual         * way of trying to unparkSuccessor of head if it needs         * signal. But if it does not,                                                                                                                                                                                                                                           */        for (;;) {            Node h = head;             if (h != null && h != tail) {                int ws = h.waitStatus;                if (ws == Node.SIGNAL) {  // 唤醒状态,表明前面排队的须要被唤醒了                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))                        continue;            // loop to recheck cases                    unparkSuccessor(h); // 唤醒前面排队的人                }                else if (ws == 0 &&  // 即上一段compareAndSetWaitStatus(h, Node.SIGNAL, 0),而后其他人又被调用了该办法                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) // 将其状态设置为可流传,使其后续(被唤醒的人再获取车票失败后,能够再尝试获取一次,详情见shouldParkAfterFailedAcquire办法)                    continue;                // loop on failed CAS            }            if (h == head)                   // loop if head changed                break;        }    }

对于doReleaseShared简略阐明下,为什么有compareAndSetWaitStatus(h, 0, Node.PROPAGATE)这个逻辑呢?因为共享模式下,可能会有多集体同时退票,这时候,依照候补队列FIFO的准则,那只能告诉第一个人能够去候补了,然而这个候补的人可能没获取票(可能被非凡人先获取车票)。因为之前是多集体同时退票的(在候补失败后,这个时候又有人退票了),那么对于这次没有获取到车票的人,再获取一次车票是很有可能会胜利的。所以说Node.PROPAGATE这个状态示意就像是,你在尝试候补失败的过程中又有人退票了,所以你再发动一次候补,胜利机会 很大。