09-Java多线程-4、AQS-同步状态的获取与释放(共享式)

AQS - 同步状态的获取与释放(共享式)

  • 同步状态的获取与释放2大类,共享式和独占式,主要针对的就是第一篇文章中所描述的几个独占式模板方法。
    表格所示如下:

  • 子类重写的流程方法所示如下:

方法作用共享式
获取同步状态tryAcquireShared(int arg)
释放同步状态tryReleaseShared(int arg)
判断是否被当前线程所独占isHeldExclusively()
  • AQS中模板方法所示如下:
方法作用共享式
获取同步状态acquireShared(int arg)
获取同步状态(可响应中断)acquireSharedInterruptibly(int arg)
获取同步状态(可超时)tryAcquireSharedNanos(int arg, long nanosTimeout)
释放同步状态releaseShared(int arg)
  • 在同步状态的获取与释放的部分,AQS采用了模板方法的设计模式,子类继承ASQ然后重写对应的流程方法,AQS中的模板方法会调用对应重写的流程方法,线程队列的处理等细节已经
    在AQS中实现好了。AQS面对的是锁和其他同步组件的实现者,继承AQS可以很方便的实现自定义的锁。

一、共享式获取同步状态

  • 下面我们分析独占式获取共享状态的方法中,有不支持中断的,支持中断和支持超时参数的,这些方法的都在AQS中实现了基本的方法流程骨架,将不能确定的逻辑部分再流程方法中
    由子类实现,因此交给流程方法由子类实现。

1.1 不支持中断的acquireShared(int arg)

1.1.1 acquireShared(int arg)
    /**
     * 共享模式获取同步状态,忽略中断(不响应中断)
     */
    public final void acquireShared(int arg) {
        //1.调用流程方法,共享式获取同步状态,获取成功就返回了,获取失败就调用doAcquireShared
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
1.1.2 doAcquireShared
/**
     * 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 (; ; ) {
                //1.获取前驱节点
                final Node p = node.predecessor();
                //2.如果前驱是head,并且获取成功(tryAcquireShared返回值大于0表示获取成功)
                //那么就设置相关变量,并返回
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //3.如果前驱不是头结点,那么自己就没有机会,就维护节点内部状态,并且park挂起进程
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            //4.取消
            if (failed)
                cancelAcquire(node);
        }
    }
  • shouldParkAfterFailedAcquire和parkAndCheckInterrupt、cancelAcquire在上一篇独占式的文章中已经分析,2种模式下这几个方法是一样的,就不过多
    分析了。

1.2 支持中断的acquireSharedInterruptibly(int arg)

1.2.1 acquireSharedInterruptibly(int arg)
  • acquireShared(int arg)是不支持中断的,acquireSharedInterruptibly支持中断,其实方法主体2者差不多,区别在doAcquireSharedInterruptibly里面收
    到中断信号后抛出异常,但是acquireShared里面的主体的doAcquireShared不支持中断(只是记录了一下)。
    /**
     * 共享式获取同步状态的自旋方法,可以响应中断,
     */
    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
        //1.中断了就抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //2.也是先尝试获取,获取成功了就返回,获取失败了就调用doAcquireSharedInterruptibly自旋获取,并且在自旋
        // 中支持对中断信号的处理
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
1.2.2 doAcquireSharedInterruptibly(int arg)
  • 共享式支持中断的同步状态获取主要流程在doAcquireSharedInterruptibly中实现,其实看过之前的独占式我们再看共享式其实差不多,支持中断无非是在for
    循环里面自旋的时候,处理中断信号,主体逻辑和doAcquireShared几乎一模一样,对比上一篇博客,其实独占式和共享式,处理流程方法不同之外,模板方法都
    是差不多的。
/**
     * Acquires in shared interruptible mode.
     *
     * @param arg the acquire argument
     * 共享式支持中断的同步状态获取主要流程实现
     * 和不支持中断的主体实现方法doAcquireShared逻辑几乎一模一样,只是在接收到
     * 中断信号时抛出异常,而doAcquireShared只是记录一下
     */
    private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (; ; ) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //如果中断了不是记录变量,而是抛出异常
                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

1.3 支持超时的tryAcquireSharedNanos(int arg, long nanosTimeout)

1.3.1 tryAcquireSharedNanos(int arg, long nanosTimeout)
  • 支持超时的共享式同步状态获取方法,主体方法和之前的doAcquireShared、doAcquireSharedInterruptibly都差不多。
    /**
     * 支持超时的共享式同步状态获取方法,
     */
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //如果tryAcquireShared(arg) >= 0说明成功直接返回,如果失败就调用doAcquireSharedNanos
        //方法,方法逻辑和不响应中断的。响应中断的,三者都差不多
        return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout);
    }
1.3.2 doAcquireSharedNanos(int arg, long nanosTimeout)
  • 主体逻辑和doAcquireShared、doAcquireSharedInterruptibly都差不多,增加了对超时逻辑的处理,参考上一篇独占式的分析中的doAcquireNanos。
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            //主体方法和之前的doAcquireShared、doAcquireSharedInterruptibly都差不多。
            //和独占式的支持超时的逻辑也差不多
            //和文章03-Java多线程、线程等待通知机制的超时范式逻辑相仿
            for (; ; ) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

二、共享式释放同步状态

2.1.1 releaseShared(int arg)

  • 共享式释放是一个并发的操作,因此需要考虑线程安全。
    /**
     * 共享式同步状态释放方法,返回值取决于tryReleaseShared流程方法的返回值
     */
    public final boolean releaseShared(int arg) {
        //1.尝试释放,释放失败就进入doReleaseShared
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

2.1.2 doReleaseShared()

  • 共享式释放同步状态,自旋保证线程安全。
private void doReleaseShared() {
        //1.自旋直到释放成功
        for (; ; ) {
            Node h = head;
            //2.队列不为空且有后继结点
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                //3.不管是共享还是独占只有结点状态为SIGNAL才尝试唤醒后继结点
                if (ws == Node.SIGNAL) {
                    //将waitStatus设置为0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //唤醒后继结点
                    unparkSuccessor(h);
                } else if (     // 如果状态为0则更新状态为PROPAGATE,更新失败则重试
                        ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            // 如果过程中head被修改了则重试,没有的话就退出
            if (h == head)                   // loop if head changed
                break;
        }
    }

三、总结

  • 共享式在不状态的获取和释放,我们看到主题流程其实和独占式差不多,区别核心在于流程方法,流程方法是在子类实现的。
  • 对比几种不同的获取方法,不支持中断的,支持中断的,支持超时的,其实主体逻辑都差不多,只是在自旋的过程中增加对中断的响应
    和超时的判断处理。

四、参考

Java并发包源码学习之AQS框架(四)AbstractQueuedSynchronizer源码分析

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值