美文网首页
AbstractQueuedSynchronizer 队列同步器

AbstractQueuedSynchronizer 队列同步器

作者: 断风雨_2669 | 来源:发表于2019-03-05 22:53 被阅读0次

    AQS 共享式同步状态获取和释放

    上一篇文章中主要分析了 AQS 的独占模式对同步状态的获取和释放过程,本文主要分析下共享模式下的同步状态的获取和释放是如何实现的

    共享锁获取

    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);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    

    从 doAcquireShared 实现可以看出,共享模式下同步队列的节点在自旋的过程与独占模式基本类似,不同在于自旋过程中成功获取同步状态时的处理

    private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // Record old head for check below
        setHead(node);
        
        // 若 progagate > 0 说明可继续向下传播唤醒节点
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    

    从 setHeadAndPropagate 的实现我们可以看出在移动 head 节点之后,若满足继续往下传播唤醒的条件时将会调用 doReleaseShared 方法。

    共享锁释放

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            // 释放同步状态
            doReleaseShared();
            return true;
        }
        return false;
    }
    ``
    
    ```java
    private void doReleaseShared() {   
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    // 将 head 节点状态重置为 0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    // CAS 成功则唤醒下个节点
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 头节点指向未发生变化的时候 退出循环
            if (h == head)                   // loop if head changed
                break;
        }
    }
    

    因共享模式下,会存在多个线程同时释放同步状态的场景, doReleaseShared 通过不断的轮询和 CAS 操作保证节点的唤醒。
    我们还是以图的形式模拟下多线程释放的场景:

    image

    相关文章

      网友评论

          本文标题:AbstractQueuedSynchronizer 队列同步器

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