Java源码阅读之ReentrantLock - lockInterruptibly和tryLock方法
阅读优秀的源码是提升编程技巧的重要手段之一。
如有不对的地方,欢迎指正
转载请注明出处https://blog.lzoro.com。
碎碎念
上一篇博客阅读了Java的ReentrantLock
的lock
和unlock
,这篇分析另外三个方法lockInterruptibly
、tryLock()
和tryLock(long time, TimeUnit unit) throws InterruptedException;
嗯,闲话少说,开工。
lockInterruptibly
顾名思义,这是一个可中断的获取锁方法。
方法的描述如下:
请求锁,除非当前线程被中断。
如果没有其他线程持有锁,则当前线程获取到锁,并为锁计数加1,并且立即返回。
如果当前线程已经持有锁,则为锁计数加1,并立即返回。
如果其他线程持有锁,则当前线程将处于不可用状态以达到于线程调度目的,并且休眠直到下面两个事件中的一个发生:
①、当前线程获取到锁
②、其他线程中断当前线程
如果当前线程获取到锁,则将锁计数设置为1。
如果当前线程在方法条目上设置了中断状态或者在请求锁的时候被中断,将抛出中断异常。
阅读 Continue
public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
从源码可以看出,实现还是依赖于内部类Sync
,acquireInterruptibly
方法是Sync
的父类AbstractQueuedSynchronizer
-AQS
提供的,我们主要还是以NonFairSync
实现为主进行阅读。
继续,可以看到方法内部有两个if
判断,第一个是判断线程的中断标识,如果为true,则抛出中断异常。
另外一个if,则是去尝试获取锁,并判断获取结果,如果获取失败,则执行doAcquireInterruptibly
① public final void acquireInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (!tryAcquire(arg)) doAcquireInterruptibly(arg); } ② protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }
这里的tryAcquire
方法目的是尝试获取锁,与上一篇博客里提到的是一致的,这里不再赘述,直接看tryAcquire
失败后执行的doAcquireInterruptibly
private void doAcquireInterruptibly(int arg) throws InterruptedException { //将当前线程添加等CLH等待队列 final Node node = addWaiter(Node.EXCLUSIVE); //初始化失败标志 boolean failed = true; try { //利用循环/自旋来请求锁 for (;;) { //获取前置节点 final Node p = node.predecessor(); //如果前置节点为首节点,并且当前线程能够成功获取锁 if (p == head && tryAcquire(arg)) { //将当前节点设置为首节点 setHead(node); p.next = null; // help GC 前首节点出队,帮助GC failed = false; return; } //判断是否可以阻塞线程并做相应操作 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) //抛出中断异常 throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
基本上可以看出,跟之前分析的acquireQueued
方法是相似的。将当前线程加入CLH的等待队列后,进行循环/自旋获取锁,未获取成功则判断是否可以阻塞并做相关操作。
具体的shouldParkAfterFailedAcquire
和parkAndCheckInterrupt
请参照上一篇,此处不再赘述。
到这里lockInterruptibly
告一段落。
tryLock()
该方法也是获取锁的一种,与lock
和lockInterruptible
不同的是,tryLock()
方法会立即返回结果,如果锁可用并且当前线程能够成功获取的话,直接返回true,否则返回false。
下面具体看下ReentrantLock是怎么实现的
public boolean tryLock() { return sync.nonfairTryAcquire(1); }
还是Sync
提供实现
final boolean nonfairTryAcquire(int acquires) { //获取当前线程 final Thread current = Thread.currentThread(); //获取锁状态 int c = getState(); //如果锁未被其他线程持有 if (c == 0) { //CAS操作设置锁状态 if (compareAndSetState(0, acquires)) { //成功后设置当前线程未锁独占线程,并返回true setExclusiveOwnerThread(current); return true; } } //如果锁已经线程持有,判断是否是当前线程持有,如果是,则允许重入 else if (current == getExclusiveOwnerThread()) { //增加锁持有计数 int nextc = c + acquires; //判断重入次数是否溢出 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //设置锁持有计数 setState(nextc); //返回true return true; } //如果锁被其他线程持有,返回false return false; }
tryLcok
是立即返回结果的获取锁方式,相比之前提到的几种自旋方式,它只进行一次获取锁的尝试,成功则返回true,失败则返回false,如果场景要求只进行一次尝试,则可以采用此方法。
tryLock(long time, TimeUnit unit)
为了标题简洁,未将异常声明加上,完整的方法定义是boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
该方法可能抛出中断异常。
方法描述如下:
在给定的等待时间内并且线程没有被中断以及锁可用的情况下,去获取锁。
如果锁可用,方法会直接返回。
如果锁不可用,则当前线程将会处于不可用状态以达到线程调度目的,并且休眠直到下面三个事件中的一个发生:
①、当前线程获取到锁
②、其他线程中断当前线程
③、指定的等待时间已过
假如当前线程:
在该方法的条目上设置其中断状态或在获取锁时中断,并且支持锁获取中断时,将抛出中断异常,当前线程中断状态会被清除。
假如给定的等待时间已过,将会返回false。
下面具体阅读源码实现,方法的入参指定了等待时间,和时间的单位,有NANOSECONDS
、MICROSECONDS
、MILLISECONDS
、SECONDS
...等单位。
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireNanos(1, unit.toNanos(timeout)); }
方法的内部调用了Sync
的tryAcquireNanos
,继续往下
public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { //判断中断状态并决定是否抛出中断异常 if (Thread.interrupted()) throw new InterruptedException(); //尝试获取锁,如果成功则返回true,失败则调用doAcquireNanos进行等待 return tryAcquire(arg) || doAcquireNanos(arg, nanosTimeout); }
tryAcqure
和之前分析的是同一个方法,不再赘述。
接下来是doAcquireNanos
方法
private boolean doAcquireNanos(int arg, long nanosTimeout) throws InterruptedException { //如果给定的时间值小于等于0,则直接返回false if (nanosTimeout <= 0L) return false; //根据给定参数计算截止时间 final long deadline = System.nanoTime() + nanosTimeout; //将当前线程添加到CLH等待队列 final Node node = addWaiter(Node.EXCLUSIVE); //初始失败标志 boolean failed = true; try { //在给定时间内循环/自旋尝试获取锁 for (;;) { //取出前置节点 final Node p = node.predecessor(); //如果前置节点为首节点,并且当前线程能够成功获取锁 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC 前首节点出队,帮助GC failed = false; return true; } //判断是否等待超时,如果超时,则返回false nanosTimeout = deadline - System.nanoTime(); if (nanosTimeout <= 0L) return false; //这里判断是否可以阻塞线程并做相应操作,跟之前分析的几个方法不一样的是,这里的阻塞多了一个判断,并且是在有限时间内阻塞,类似于sleep if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold) LockSupport.parkNanos(this, nanosTimeout); //判断中断状态,并决定是否抛出异常 if (Thread.interrupted()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
从上面我们可以看出doAcquireNanos
跟doAcquireInterruptibly
以及上一篇博客提到的acquireQueued
的都有相似点,都是有自旋等待,以及阻塞判断和阻塞操作,不同的是,doAcquireNanos
的阻塞是有时间限制的,所以能在给定的时间内,返回获取锁的操作结果。
总结
lockInterruptibly是可中断的获取锁方式。
tryLock()是立即返回获取结果的获取锁方式。
tryLock(long time, TimeUnit unit)是在给定时间内,尝试获取锁的方式。
好了,告一段落。
源码也看了不少,之前写的解析也都没有整理到博客,现在终于是能抽出点时间动动手了。
如果对你有哪怕一丢丢帮助,不妨给个赞呗,溜了溜了。
我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=24hvoorha868k
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
C# 操作Excel图形——绘制、读取、隐藏、删除图形
简介 本篇文章将介绍C# 如何处理Excel图形相关的问题,包括以下内容要点: 1.绘制图形 1.1 绘制图形并添加文本到图形 1.2 添加图片到图形 1.3 设置图形阴影效果 2. 提取图形中的文本、图片 3. 设置图形的显示、隐藏 4. 删除图形 4.1删除指定图形 4.2 删除所有图形 所需工具 Free Spire.XLS for .NET 8.3 (社区版) PS: 下载安装该类库后,注意在项目程序中添加引用Spire.Xls.dll文件(dll文件可在安装路径下的Bin文件夹中获取) 注:Spire.xls能支持的图形种类很多,常见的Office Excel中的图形,这个类库也都能实现, 示例代码(供参考) 1. 绘制图形 【C#】 using System.Drawing; using Spire.Xls; using Spire.Xls.Core; namespace Add_shapes_to_Excel { class Program { static void Main(string[] args) { //创建实例 Workbook workbook ...
- 下一篇
区块链开发公司谈公有链是区块链未来的趋势
公有链通常也称为非许可链,任何人都可以参与区块链数据维护和读取,容易部署应用程序,完全去中心化不受任何机构控制。 公有链是真正意义上的完全去中心化的区块链,它通过密码学保证交易不可篡改,同时也利用密码学验证以及经济上的激励,在互为陌生的网络环境中建立共识,从而形成去中心化的信用机制。 在上海举办的“2018中国金融科技发展论坛”上,蚂蚁金服副总裁俞胜法在演讲中表示,区块链的发展方向是公有链,公有链很重要,特别是对C端的应用场景,并断言“以后肯定会有一个爆发”。 蚂蚁金服副总裁俞胜法分享了蚂蚁金服在区块链领域的“三做”、“三不做”军规。 具体来说,要做的包括 :第一,是对社会有价值的事情;第二,专注于区块链的技术特别是在应用当中的技术障碍需要消除;第三,建立区块链生态,希望和生态伙伴一起做区块链。 不做的包括:第一,不做空气币;第二,不做违法技术应用;第三,不做对于用户隐私和数据安全方面的行为。 他表示,“蚂蚁金服自己也会做一些IP的管理,比如说我们对于版权,音乐版权,影视版权,这是以后很大的发展方向。我们前段时间利用区块链,在香港发布了区块链技术的跨国对话网络。我们认为跨境的支付网络还...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- 设置Eclipse缩进为4个空格,增强代码规范
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Docker使用Oracle官方镜像安装(12C,18C,19C)