AQS(2):acquire

在上文讲到,AQS中需要支持阻塞与释放两类操作,那么最重要的自然是acquire和release了。

对于acquire,内部会调用tryAcquire()方法,而tryAcquire是一个protected的方法,在本抽象类中并未给出实现。因此这篇文中主要会讨论除调用tryAcquire方法外还需要做哪些操作。具体的tryAcquire后面也会继续探究。

我们先来说一下acquire方法的思路,首先tryAcquire方法如果成功,那没的说直接返回true就好。如果失败了,那么需要再队列中插入一个新节点以表示当前线程。加入队列后我们来看他是否加在头结点的后面,如果是那么他可以立即尝试一波tryAcquire。这次try中如果他成功了,那么当前节点就可以作为头结点了(同时释放前头结点)。如果前驱不是头结点或者try失败了,那么就要考虑是不是需要阻塞线程。

如何判断呢?那就是如果前驱节点的waitState是SIGNAL,那么意味着后继节点正在或者将要阻塞,那么当前线程可以安全的阻塞。如果waitState大于0,那么这个前驱代表的是一个被取消的线程,那我们就接着向前找,直到找到一个有效节点,此时需要重新判断其前驱是不是头结点,后续如上所述。那如果小于0,就需要用CAS操作设置前驱状态为SIGNAL表示自己将处于阻塞状态,然后再重新检查前驱是不是头结点重新try一下什么的,也是之前描述的流程。为了更好地理解,可以参照下面的图示:

acquire

然后我们再来看一下acquireInterruptibly方法,该方法无非多了一个中断则取消请求,因此在方法流程上并没有什么区别:

acquireInterruptibly

可以看到,在acquire中,如果park操作被中断,那么只是记录了interrupted状态,然后继续进入循环判断是否可以acquire或者阻塞。而在acquireInterruptibly中,一旦被中断,那么就立即抛出InterruptedException异常。其他方面两个方法并没有显著不同。

接下来是tryAcquireNanos方法。可以被中断,增加了超时则失败的功能。可以说该方法的实现与上述两方法没有任何区别。那时间功能是如何实现的呢?

nanosTimeout = deadline - System.nanoTime();
if (nanosTimeout <= 0L)    
    return false;
if (shouldParkAfterFailedAcquire(p, node) &&    nanosTimeout > spinForTimeoutThreshold)    
    LockSupport.parkNanos(this, nanosTimeout);

如果剩余时间小于0那么acquire失败,如果该时间大于一次自旋锁时间(1000L),并且可以被阻塞,那么调用LockSupport.parkNanos方法阻塞线程。

对于共享模式下的acquire,其区别主要是使用了tryAcquireShared,该方法是一个需要子类实现的方法。

那么对于acquire的方法实现这一章就讲完了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值