Java并发编程——AQS之共享式同步状态的获取与释放

共享式获取

void acquireShared(int arg)

//同步器调用tryAcquireShared获取同步状态,当返回值大于0 时,表示能够获取同步状态。
public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

int tryAcquireShared(arg)

//如果获取同步状态失败,那么返回的就是负数。
//在acquireShared 方法中,只有获取同步状态失败,才能执行doAcquireShared 方法
protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

void doAcquireShared(int arg)

 /**
     * Acquires in shared uninterruptible mode.
     * @param arg the acquire argument
     */
    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);
                    //如果返回值 r >= 0,表示该次获取同步状态成功并从自旋过程中退出。
                    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);
        }
    }

void setHeadAndPropagate(Node node, int propagate)

 private void setHeadAndPropagate(Node node, int propagate) {
        Node h = head; // 记录旧的头节点
        //将当前操作的节点设置为头结点
        setHead(node);
       //如果propagate > 0,表示当前同步状态获取成功;
       //或者waitStatus 被设置为PROPAGATE(PROPAGATE = -3;)。
//注意这里h == null || h.waitStatus < 0 
//与 (h = head) == null || h.waitStatus < 0的区别:
//第一个判断是在线程执行 setHead(node);方法之后,才走到这里。
//第二个判断是线程执行 setHead(node);方法之前,就已经走到这里了。
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
			//向后传播
            Node s = node.next;
           // 如果下个节点不存在 或者 是在共享模式下等待,
           //那么我们都要执行doReleaseShared();方法,
            //释放同步状态。
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

boolean isShared()

  /**
         * Returns true if node is waiting in shared mode.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

void setHead(Node node)

 /**
     * Sets head of queue to be node, thus dequeuing. Called only by
     * acquire methods.  Also nulls out unused fields for sake of GC
     * and to suppress unnecessary signals and traversals.
     *
     * @param node the node
     */
    private void setHead(Node node) {
        head = node;
        node.thread = null;
        node.prev = null;
    }

void doReleaseShared()

/**
     * Release action for shared mode -- signals successor and ensures
     * propagation. (Note: For exclusive mode, release just amounts
     * to calling unparkSuccessor of head if it needs signal.)
     */
    private void doReleaseShared() {
       
        for (;;) {
        //节点h表示头结点
            Node h = head;
            //头结点不为空 且 头结点和尾结点不是同一个节点
            //也就是,同步队列中不只一个节点。
            if (h != null && h != tail) {
            //ws是当前头节点的waitStatus的值
                int ws = h.waitStatus;
                //如果ws的值表示可唤醒后继节点
                if (ws == Node.SIGNAL) {
                	如果CAS操作设置节点waitStatus的值为0 失败
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    //从for 循环头开始自旋
                        continue;            // loop to recheck cases
                    //只有当头结点的waitStatus的值为SIGNAL,且CAS修改waitStatus成功,
                   // 才可以唤醒后继节点
                    unparkSuccessor(h);
                }
                //如果waitStatus == 0 且CAS修改节点waitStatus值为PROPAGATE失败
                //从for 循环头开始自旋
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            //unparkSuccessor(h);传入的参数是h,
            //如果该方法执行成功,那么h所指的就应该是当前的头结点
            if (h == head)                   // loop if head changed
                break;
        }
    }

void unparkSuccessor(Node node)

private void unparkSuccessor(Node node) {
      
        int ws = node.waitStatus;
        //如果waitStatus值是负的,那么就要尝试在唤醒之前,修改
        //如果没有修改成功也没没关系
        //如果status被等待中的线程修改也可以
        //如果此时waitStatus<0,使用CAS将waitStatus设为0,
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

       
         //找到当前节点的后继节点s
        Node s = node.next;
       // 如果s为空(node不存在后继节点)或者s.waitStatus > 0(s节点已经被唤醒了)
        if (s == null || s.waitStatus > 0) {
        //此时再将s置位空,从尾结点开始,逆向查找,
        //直到找到waitStatus<=0 的节点(也就是找到第一个非空节点);
        //判断当前的额非空节点t的waitStatus <=0,如果是这样,说明t还没有被唤醒。
        //将t的值给s
        //再次判断s是否为空,以防别的线程已经修改过。
        //之后再执行unpark,获取同步状态。
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

全图

在这里插入图片描述

共享式释放

boolean releaseShared(int arg)

//在共享模式下释放同步状态,该方法释放完同步状态之后,会唤醒后续处于等待状态的节点。
public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

boolean tryReleaseShared(int arg)

共享模式下release是否允许一个等待中的acquire(共享的或排他的)获取后继节点。
如果是,返回true
否则返回false;
简而言之,就是是否允许对后继节点的获取。

    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值