J.U.C之AQS:源码解析共享式同步实现

本文围绕共享式同步状态展开,详细介绍了获取和释放的过程。获取时创建共享式节点加入CLH队列,经自旋、判断前置节点等操作,成功则设置当前节点为head并唤醒后置节点;释放时进入自旋,根据head节点状态操作,直至队列无等待节点或释放节点获取失败。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

获取共享式同步状态

总体图

 

入口函数

public final void acquireShared(int arg) {
       /*
        *子类实现tryAcquireShared能否获取的共享式同步状态
        *如果返回>=0则获取同步状态成功方法直接返回
        *如果返回< 0则获取同步状态失败进入if语句
        */
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

doAcquireShared

  • 1 创建节点node(类型是共享式),添加到CLH同步队列尾部,节点等待状态为0(同独占式同步相同)
  • 2 进入自旋(同独占式同步相同)
  • 3 判断当前节点前置节点是否为head节点,如果是重新尝试调用tryAcquireShared获取同步状态(同独占式同步相同)
  • 3-1-1 获取同步状态失败,获取同步状态失败,就将当前节点和前驱节点作为参数交给shouldParkAfterFailedAcquire调用设置同步节点node中等待状态,如果shouldParkAfterFailedAcquire返回true,则调用parkAndCheckInterrupt()阻塞当前线程。如果shouldParkAfterFailedAcquire返回false,则重新进入步骤 2 自旋 (同独占式同步相同)
  • 3-1-2 如果节点线程被从阻塞中唤醒重新进入 步骤 2(自旋)
  • 3-2-1 如果调用tryAcquireShared获取同步状态成功,设置当前节点为head节点.
  • 3-2-2 ==会判断CLH队列中是否存在等待的共享节点.如果存在则会调用doReleaseShared,唤醒新head节点后置节点阻塞,被唤醒的节点会重新进入自旋,再次调用tryAcquireShared判断获取同步状态,如果成功则继续向下传播.直到某一个节点获取同步状态失败进入阻塞或者CLH队列不存在等待的节点==.(同独占式同步不相同)
  • 3-2-3 并释放当前节点前置节点的指针,retun跳出自旋(同独占式同步相同)
private void doAcquireShared(int arg) {
    //创建节点node(类型是共享式)
    //如果CLH同步队列初始化则将当前节点添加到CLH同步队列尾部,
    //如果CLH同步队列没有初始化则需要调用子函数enq创建一个CLH同步队列并将当前节点添加到CLH同步队列尾部.
    final Node node = addWaiter(Node.SHARED);
    //是否失败
    boolean failed = true;
    try {
        //是否被中断
        boolean interrupted = false;
        for (;;) {
            // 1. 获得当前节点的先驱节点
            final Node p = node.predecessor();
            // 2. 如果当前节点的先驱节点是头结点,则再次尝试调用子类实现tryAcquireShared,
            //判断能否获取的共享式同步状态   
            if (p == head) {
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    //将当前节点设置为head,同时只要CLH队列中存在等待的节点,且节点为共享节点则会        
                    //调用doReleaseShared,唤醒head节点后置节点阻塞去竞争同步状态.
                    setHeadAndPropagate(node, r);
                    //释放当前节前驱节点指针(这里前驱节点也相当于原始的head节点)等待GC回收
                    p.next = null; // help GC
                    if (interrupted)
                        selfInterrupt();
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        //如果失败则设置当前节点等待状态为取消
        if (failed)
            cancelAcquire(node);
    }
}

释放共享式同步状态

 

入口函数

public final boolean releaseShared(int arg) {
         /*
        *子类实现能否释放的共享式同步状态
        *如果返回true则表示释放同步状态准入条件成功进入if语句
        *如果返回false则表示释放同步状态失败返回false
        */
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

doReleaseShared(核心)

  • 1 进入自旋
  • 2 获取当前head节点,判断等待队列中是否存在等待节点,不存在进入步骤4直接退出.
  • 3 获取当前head节点的等待状态
  • 3-1 如果当前节点的状态为-1(存在等待的后置节点),使用CAS设置其为0(使用CAS失败进入自旋重新设置)同时会调用unparkSuccessor从head开始选择被唤醒线程节点,唤醒其中阻塞的线程.
  • 3-2 如果当前节点的状态为0(存在后置节点已经取消等待),使用CAS设置其为PROPAGATE(使用CAS失败进入自旋重新设置),
  • 4 判断3-1唤醒线程是否获取同步状态(我们在进入自旋时候将head节点设置给了变量h,如果在步骤3-1-1)被释放的线程在doAcquireShared获取同步状态,会变更head节点,此时head节点状态会改变,原始head节点已经出队,head节点指向了获取同步状态节点和原始head节点指向不同),获取同步状态重新进入自旋,释放新head后置节点阻塞的线程.直到同步队列中不存在等待的节点.或者某个释放的节点获取同步状态失败.
private void doReleaseShared() {
        //1.进入自旋
        for (;;) {
            //2.获取当前head节点,判断等待队列中是否存在等待节点,不存在进入步骤4直接退出.
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //3-1 如果当前节点的状态为-1(存在等待的后置节点),使用CAS设置其为0(使用CAS失败进入自 
                //旋重新设置)同时会唤醒head后置节点中线程从阻塞中释放.
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //3-1-1
                    unparkSuccessor(h);
                }
                //3-2 如果当前节点的状态为0(存在后置节点已经取消等待),使用CAS设置其为PROPAGATE
                //(使用CAS失败进入自旋重新设置),
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            /**
            *
            *4. 判断3-1唤醒线程是否获取同步状态(我们在进入自旋时候将head节点设置给了变量h,
            *如果在步骤3-1-1)被释放的线程在doAcquireShared获取同步状态,会变更head节点,
            *此时head节点状态会改变,原始head节点已经出队,head节点指向了获取同步状态节点和
            *原始head节点指向不同),获取同步状态重新进入自旋,释放新head后置节点阻塞的线程.
            *直到同步队列中不存在等待的节点.或者某个释放的节点获取同步状态失败.
            **/
            if (h == head)                   // loop if head changed
                break;
        }
    }



作者:贪睡的企鹅
链接:https://www.jianshu.com/p/c9433438b119
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值