JUC之AQS:共享锁部分

AQS有两种模式,一种是独占模式,一种是共享模式。这篇文章是关于共享模式的原理。

  1. 当线程调用acquireShared()申请获取锁资源时,如果成功,则进入临界区。
  2. 当获取锁失败时,则创建一个共享类型的节点并进入一个FIFO等待队列,然后被挂起等待唤醒。
  3. 当队列中的等待线程被唤醒以后就重新尝试获取锁资源,如果成功则唤醒后面还在等待的共享节点并把该唤醒事件传递下去,即会依次唤醒在该节点后面的所有共享节点,然后进入临界区,否则继续挂起等待。

tryAcquireShared应该在子类实现,返回值小于0表示获取锁失败,需要进入等待队列。其二、如果返回值等于0表示当前线程获取共享锁成功,但它后续的线程是无法继续获取的,也就是不需要把它后面等待的节点唤醒。最后、如果返回值大于0,表示当前线程获取共享锁成功且它后续等待的节点也有可能继续获取共享锁成功,也就是说此时需要把后续节点唤醒让它们去尝试获取共享锁。


    public final void acquireShared(int arg) {

        if (tryAcquireShared(arg) < 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) {//当前节点获取锁成功,并可能可以向后传递
                        setHeadAndPropagate(node, r);//唤醒后续节点,同时将head节点置为当前的节点。
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //和独占锁的逻辑一样
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

release

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {//tryReleaseShared应该在子类中实现
            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, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        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, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    } 

Semaphore具体实现的例子

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);//请求一个令牌
    }

    //公平锁版本(需要检查有没有前面的排队节点)
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

    //不公平版本(不需要检查有没有排序的节点)
    final int nonfairTryAcquireShared(int acquires) {
       for (;;) {
           int available = getState();
           int remaining = available - acquires;
           if (remaining < 0 ||
               compareAndSetState(available, remaining))
               return remaining;
       }
   }
    原文作者:JUC
    原文地址: https://blog.csdn.net/define_us/article/details/80819813
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞