【转载】AQS中的cancelAcquire何时会运行

大家好,我是江湖人送外号[道格牙]的子牙老师。

今天分享一篇JUC包中AQS的一个方法的深度解读,可能你都没有关注过这个细节吧。

这篇文章适合对AQS有一定基础的童鞋学习。学完本篇文章就可以对AQS高级部分有更深入的理解:比如AQS中的节点什么时候会修改自己的waitStatus、Node.CANCELLED状态有何意义、cancelAcquire何时会运行……

尤其是cancelAcquire何时会运行,最近讲完AQS课被问得比较多。今天抽个空写篇文章分享下。cancelAcquire这个方法内部做了什么,后面写文章分享,这个方法的代码也很难理解。

国际惯例,先上代码。

这两段代码是AQS的源码,因为AQS被很多类共用,本篇文章是从ReentrantLock角度进行分析。

分析问题

当failed为true时才会执行方法cancelAcquire,那什么情况下failed为true呢?try代码段执行过程中出现异常。

那什么情况下try代码段执行过程中会出现异常呢?整个流程看下来,只有两个地方有可能:predecessor、tryAcquire。看下源码,都会抛出异常。

那真正导致try代码段执行过程中出现异常是因为哪一个呢?我们来逐个分析下。

如果是因为tryAcquire

肯定不可能。为什么这么说呢?看代码

如果tryAcquire没有被重写,程序根本执行不到方法acquireQueued。因为不会有线程拿到锁,不会有线程因抢不到锁入队列。

如果是因为predecessor

其实也不可能。虽然这里有抛出异常的代码,但是这段代码永远不会执行到。所以注释里有这句话。

The null check could be elided, but is present to help the VM

空检查可以省略,但存在是为了帮助VM

为什么说抛出异常那段代码永远不会执行到呢?方法predecessor是入队后执行的,AQS队列有这样的特点:入队后至少有两个节点,第一个节点永远是占有锁的线程对应的节点。

第三种可能

刚刚分析完我们好不容易找到的两条线索,经过缜密的分析,得出结论都不可能。于是有了第三种可能:第二个节点加锁失败,没有机会将failed改为true。有没有可能呢?来分析下

先回答第一个问题:什么时候第二个节点加锁失败呢?答案是非公平锁的时候。即占有锁的线程刚释放完锁,刚唤醒第二个节点或者,这时候恰好来了一个线程拿到了锁,这时候唤醒的第二个节点来抢锁就会抢锁失败。

虽然抢锁失败了,但是也不会执行到finally代码段,而是重新自旋设置闹钟,然后调用park阻塞自己等待再次唤醒。

结论一

你还能想到其他可能吗?我是想不到了。那cancelAcquire永远不会执行吗?

在ReentrantLock中,不管是公平锁还是非公平锁,cancelAcquire都不会运行。那道格李为什么这样写呢?这个有点像编译器编译sync时会生成两个monitorenter,一种保险策略吧。

还有一种可能:有可能是保持代码统一,反正用到的时候会用到,用不到的时候也不会被执行到。有的童鞋可能说,道格李这样的大神不可能犯这种低级错误吧。我倒不觉得这是一种低级错误,保持统一反映了代码洁癖、反映了先有思想后有代码。

结论二

那什么情况下方法cancelAcquire会运行呢?响应中断的程序中,句式如

推荐阅读

 1、深入剖析Lambda表达式的底层实现原理

2、今天聊点不一样的,百万年薪需要具备的能力

3、啊,i++与++i居然有这么多学问在里面

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值