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

独占式获取acquire

void acquire(int arg)

/**
*这里的acquire方法目的就是在获取失败时,能够自旋
*这里的if判断就实现了这一功能
*如果想要产生自旋,那么就要同时满足if的两个条件
*1.tryAcquire(arg)方法是由执行线程调用的,
*如果这个方法失败,那么acquire方法可能会让线程排队(如果它还没有排队),直到其他线程释放它为止。
*2.同时,acquireQueued成功,就是当前获取失败的线程进入排队成功——也就是说当前线程被中断
*并且处在同步队列中。
*那么此时需要该节点所在的线程自旋。
*/
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
              
               /** 如果P指向头结点(同步队列中从头结点出队,也就是说,
                如果按顺序唤醒,那么最先唤醒的一定是头结点)
                并且头结点尝试获取同步状态成功。
                那么将当前节点设置为头结点,
                p重新定位到当前的头结点(也就是node的位置);
                failed 如果是true表示从队列中获取节点失败,
                此时我们已经从队列中获取了当前节点的信息,所以此时将failed设置为false。
                此时acquireQueued返回值是false,说明在队列中获取线程同步状态没有遇到中断。
                也就是说,不需要节点内的线程自旋。
                */
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                /**
                *如果当前节点的前驱节点 的线程状态是SIGNAL,也就是需要唤醒当前节点时:
                *如果唤醒失败,那么此时需要将当前节点的线程阻塞。
                *也就是shouldParkAfterFailedAcquire返回true。
                *此时我们希望当前节点的线程被中断。
                *接下来执行中断操作:
                *尝试中断当前线程,如果中断成功则parkAndCheckInterrupt()返回true。
                **/
                这是一个从同步队列中获取当前线程的同步状态失败时,判断中断是否成功。
                
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
        /*
        无论如何都会执行这一步
        当从同步队列中获取节点失败是,failed的值是true,那么就需要执行cancelAcquire方法,
        终止尝试获取同步队列中的同步状态。
        如果顺利从队列中获取当前线程的同步状态,那么久不需要执行停止尝试获取的操作。
        */
            if (failed)
                cancelAcquire(node);
        }
    }

全图

在这里插入图片描述

主干

在这里插入图片描述

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

在这里插入图片描述

shouldParkAfterFailedAcquire(p, node)

在这里插入图片描述

独占式释放release

boolean release(int arg)

使用release方法可以释放节点的同步状态。
public final boolean release(int arg) {
/*尝试release
如果成功并且头结点不为空&&头结点的state值不是0(也就是头结点不属于四种状态中的任何一种);
那么需要将后继节点解除阻塞(会唤醒后继节点,进而使后继节点重新尝试获取同步状态);
返回true。
如果尝试release失败,则直接返回false。


*/
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

在这里插入图片描述

总结

获取同步状态时,同步器维护一个同步队列,获取状态失败的线程都会被加入到同步队列中,
并且会在同步队列中自旋;
移出队列或停止自旋的条件是:前驱节点为头节点且成功获取同步状态。
p == head && tryAcquire(arg)。

释放同步状态时,同步器调用tryRelease(arg)方法释放同步器,然后unparkSuccessor(h)唤醒头结点的
后继节点。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值