并发编程系列(十一)AQS同步器共享锁加锁解锁源码解读

独占模式和共享模式的最大的不同就是在同一时刻能否有多个线程获取同步状态,

独占模式:获取资源后,只有一个线程获取同步状态并执行

共享模式在获取资源后,多个线程共同执行

1.acquireShare() 方法源码解析

此方法是共享模式下线程获取资源的顶层入口,获取成功则直接返回,失败则进入等待队列,并且自旋知道获取资源为止

/**
*    以共享模式获取资源。忽略中断
*/    
public final void acquireShared(int arg) {
        //该方法只有一个分支,判断是否能获取到共享资源
        //能够获取到则返回正数,否则,返回负数
        if (tryAcquireShared(arg) < 0)
            //没有获取到公共资源,将执行此方法
            doAcquireShared(arg);
    }

2. doAcquireShared()方法解析

    /**
    *加入等待队列
    *以共享非中断获取同步状态
    */
    private void doAcquireShared(int arg) {
        //自旋加入队尾,与独占模式类似,只是传入的参数不同
        //独占模式夏安安传入参数node.EXCLUSIVE
        //共享模式下传入参数Node.SHARED
        final Node node = addWaiter(Node.SHARED);
        //标记是否中断
        boolean failed = true;
        try {
            boolean interrupted = false;
            //自旋开始
            for (;;) {
                // 获取Node结点的前驱节点
                final Node p = node.predecessor();
                //判断结点的前驱节点是否为头节点,如果是,尝试获取共享资源
                if (p == head) {
                    //尝试获取共享资源
                    int r = tryAcquireShared(arg);
                    //如果r>=0表示获取共享资源成功
                    if (r >= 0) {
                        //将当前结点设置为头结点,检查后继节点是否在共享模式下等待
                        setHeadAndPropagate(node, r);
                        //原头结点的next引用置为null,方便GC
                        p.next = null; // help GC
                        //如果线程被中断
                        if (interrupted)
                            //维护中断状态
                            selfInterrupt();
                         //标记failed为fasle
                        failed = false;
                        return;
                    }
                }
                //与独占模式下操作类似
                //校验是否需要阻塞线程,判断中断状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

和acquireQueue()很相似,大概流程基本一样,只不过这里将补偿的操作selfInterrupt()放到doAcquireShared()里了,而独占模式是放到

acquireQueue里

与独占模式相比,还有一点需要注意,这里只有线程head.next时(即结点是头结点的直接后继节点),才会去尝试获取资源,如果还有其他结点还会唤醒之后的node

3. setHeadAndPropagate()方法解析

    /**
    *成为头结点,唤醒后继节点
    */ 
     private void setHeadAndPropagate(Node node, int propagate) {
        //获取队列头结点
        Node h = head; // Record old head for check below
        //把当前结点设置为头结点
        setHead(node);
        /*
         * 三种可以唤醒node的操作
         *    1propagate > 0代表后继节点需要被唤醒
         *    2原头结点h为空或者ws<0
         *    3新的头结点为空或者新的头结点的ws<0
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            //找到当前结点的后继节点s
            Node s = node.next;
           //s=null或者s是共享模式,调用doReleaseShared方法唤醒后继节点
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

4.doReleaseShared()方法解析

    /**
    *共享模式的释放操作
    */
   private void doReleaseShared() {
        /*
         *自旋释放后继节点
         */
        for (;;) {
            //如果是头结点非空并且头结点不等于尾结点:队列中至少两个节点
            Node h = head;
            if (h != null && h != tail) {
                //获取头结点的waitstatus
                int ws = h.waitStatus;
                //状态是SIGNAL
                if (ws == Node.SIGNAL) {
                    //变量h有waitStatus通过CAS设置为初始状态0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        //如果CAS失败,执行continue进入下一个循环
                        continue;            // loop to recheck cases
                    //如果cas成功
                    //唤醒后继节点
                    //执行过程佟独占模式下的唤醒流程
                    unparkSuccessor(h);
                }
                //如果h的ws=0就把h设置为PROPAGATE,表示可以向后传播
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            //自旋跳出结果,head不变则跳出自旋,head变化则一直自旋
            //如果头结点没有发生变化,表示设置完毕,可以退出循环
            //如果头结点发生变化,可能被唤醒的其他结点重新设置了头结点
            //这样头结点发生了改变,要进行重试,保证可以传播唤醒信号
            if (h == head)                   // loop if head changed
                break;
        }
    }

总体来说acquireShared():尝试获取资源,成功直接返回则;失败则通过doAcquireShared进入等待队列,直接被unpak()/interrupt()

并成功获取到资源才返回,整个等待过程也是忽略中断的

5. 共享锁释放锁

releaseShare()方法解析:释放锁

 /**
  *共享模式释放资源
  */    
public final boolean releaseShared(int arg) {
        //尝试释放公共资源
        if (tryReleaseShared(arg)) {
            //尝试释放资源成功执行,doReleaseShared放发
            //同acquireShared方法调用doReleaseShared方法类似
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared()方法同样是空方法,是预留给子类去实现的

    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
发布了55 篇原创文章 · 获赞 3 · 访问量 5250

猜你喜欢

转载自blog.csdn.net/qq_38130094/article/details/103646505